mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-07-14 06:00:33 +03:00
Merge branch 'master' into init/pythonPackages.mkdocs-git-authors-plugin
This commit is contained in:
commit
59eb55b5d6
3435 changed files with 97704 additions and 95769 deletions
18
.github/CODEOWNERS
vendored
18
.github/CODEOWNERS
vendored
|
@ -23,7 +23,7 @@
|
|||
|
||||
# Libraries
|
||||
/lib @edolstra @infinisil
|
||||
/lib/systems @alyssais @ericson2314 @matthewbauer
|
||||
/lib/systems @alyssais @ericson2314 @matthewbauer @amjoseph-nixpkgs
|
||||
/lib/generators.nix @edolstra @Profpatsch
|
||||
/lib/cli.nix @edolstra @Profpatsch
|
||||
/lib/debug.nix @edolstra @Profpatsch
|
||||
|
@ -37,10 +37,10 @@
|
|||
/pkgs/top-level/stage.nix @Ericson2314 @matthewbauer
|
||||
/pkgs/top-level/splice.nix @Ericson2314 @matthewbauer
|
||||
/pkgs/top-level/release-cross.nix @Ericson2314 @matthewbauer
|
||||
/pkgs/stdenv/generic @Ericson2314 @matthewbauer
|
||||
/pkgs/stdenv/generic @Ericson2314 @matthewbauer @amjoseph-nixpkgs
|
||||
/pkgs/stdenv/generic/check-meta.nix @Ericson2314 @matthewbauer @piegamesde
|
||||
/pkgs/stdenv/cross @Ericson2314 @matthewbauer
|
||||
/pkgs/build-support/cc-wrapper @Ericson2314
|
||||
/pkgs/stdenv/cross @Ericson2314 @matthewbauer @amjoseph-nixpkgs
|
||||
/pkgs/build-support/cc-wrapper @Ericson2314 @amjoseph-nixpkgs
|
||||
/pkgs/build-support/bintools-wrapper @Ericson2314
|
||||
/pkgs/build-support/setup-hooks @Ericson2314
|
||||
/pkgs/build-support/setup-hooks/auto-patchelf.sh @layus
|
||||
|
@ -124,7 +124,7 @@
|
|||
/doc/languages-frameworks/rust.section.md @zowoq @winterqt @figsoda
|
||||
|
||||
# C compilers
|
||||
/pkgs/development/compilers/gcc @matthewbauer
|
||||
/pkgs/development/compilers/gcc @matthewbauer @amjoseph-nixpkgs
|
||||
/pkgs/development/compilers/llvm @matthewbauer @RaitoBezarius
|
||||
|
||||
# Compatibility stuff
|
||||
|
@ -196,6 +196,11 @@ pkgs/development/python-modules/buildcatrust/ @ajs124 @lukegb @mweinelt
|
|||
/nixos/tests/kea.nix @mweinelt
|
||||
/nixos/tests/knot.nix @mweinelt
|
||||
|
||||
# Web servers
|
||||
/doc/builders/packages/nginx.section.md @raitobezarius
|
||||
/pkgs/servers/http/nginx/ @raitobezarius
|
||||
/nixos/modules/services/web-servers/nginx/ @raitobezarius
|
||||
|
||||
# Dhall
|
||||
/pkgs/development/dhall-modules @Gabriella439 @Profpatsch @ehmry
|
||||
/pkgs/development/interpreters/dhall @Gabriella439 @Profpatsch @ehmry
|
||||
|
@ -308,3 +313,6 @@ nixos/lib/make-single-disk-zfs-image.nix @raitobezarius
|
|||
nixos/lib/make-multi-disk-zfs-image.nix @raitobezarius
|
||||
nixos/modules/tasks/filesystems/zfs.nix @raitobezarius
|
||||
nixos/tests/zfs.nix @raitobezarius
|
||||
|
||||
# Linux Kernel
|
||||
pkgs/os-specific/linux/kernel/manual-config.nix @amjoseph-nixpkgs
|
||||
|
|
|
@ -34,5 +34,7 @@ The `ibus-engines.typing-booster` package contains a program named `emoji-picker
|
|||
On NixOS, it can be installed using the following expression:
|
||||
|
||||
```nix
|
||||
{ pkgs, ... }: { fonts.fonts = with pkgs; [ noto-fonts-emoji ]; }
|
||||
{ pkgs, ... }: {
|
||||
fonts.packages = with pkgs; [ noto-fonts-emoji ];
|
||||
}
|
||||
```
|
||||
|
|
4
doc/common.nix
Normal file
4
doc/common.nix
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
outputPath = "share/doc/nixpkgs";
|
||||
indexPath = "manual.html";
|
||||
}
|
|
@ -456,7 +456,7 @@ In the file `pkgs/top-level/all-packages.nix` you can find fetch helpers, these
|
|||
owner = "NixOS";
|
||||
repo = "nix";
|
||||
rev = "1f795f9f44607cc5bec70d1300150bfefcef2aae";
|
||||
hash = "ha256-7D4m+saJjbSFP5hOwpQq2FGR2rr+psQMTcyb1ZvtXsQ=";
|
||||
hash = "sha256-7D4m+saJjbSFP5hOwpQq2FGR2rr+psQMTcyb1ZvtXsQ=";
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -23,6 +23,14 @@ $ nix-shell
|
|||
|
||||
If the build succeeds, the manual will be in `./result/share/doc/nixpkgs/manual.html`.
|
||||
|
||||
## devmode {#sec-contributing-devmode}
|
||||
|
||||
The shell in the manual source directory makes available a command, `devmode`.
|
||||
It is a daemon, that:
|
||||
1. watches the manual's source for changes and when they occur — rebuilds
|
||||
2. HTTP serves the manual, injecting a script that triggers reload on changes
|
||||
3. opens the manual in the default browser
|
||||
|
||||
## Syntax {#sec-contributing-markup}
|
||||
|
||||
As per [RFC 0072](https://github.com/NixOS/rfcs/pull/72), all new documentation content should be written in [CommonMark](https://commonmark.org/) Markdown dialect.
|
||||
|
|
|
@ -62,6 +62,8 @@ Sample template for a package update review is provided below.
|
|||
- [ ] package build on ARCHITECTURE
|
||||
- [ ] executables tested on ARCHITECTURE
|
||||
- [ ] all depending packages build
|
||||
- [ ] patches have a comment describing either the upstream URL or a reason why the patch wasn't upstreamed
|
||||
- [ ] patches that are remotely available are fetched rather than vendored
|
||||
|
||||
##### Possible improvements
|
||||
|
||||
|
@ -105,7 +107,8 @@ Sample template for a new package review is provided below.
|
|||
- [ ] source is fetched using the appropriate function
|
||||
- [ ] the list of `phases` is not overridden
|
||||
- [ ] when a phase (like `installPhase`) is overridden it starts with `runHook preInstall` and ends with `runHook postInstall`.
|
||||
- [ ] patches that are remotely available are fetched with `fetchpatch`
|
||||
- [ ] patches have a comment describing either the upstream URL or a reason why the patch wasn't upstreamed
|
||||
- [ ] patches that are remotely available are fetched rather than vendored
|
||||
|
||||
##### Possible improvements
|
||||
|
||||
|
|
|
@ -214,26 +214,81 @@ The last checkbox is fits [CONTRIBUTING.md](https://github.com/NixOS/nixpkgs/blo
|
|||
- Hydra builds for master and staging should not be used as testing platform, it’s a build farm for changes that have been already tested.
|
||||
- When changing the bootloader installation process, extra care must be taken. Grub installations cannot be rolled back, hence changes may break people’s installations forever. For any non-trivial change to the bootloader please file a PR asking for review, especially from \@edolstra.
|
||||
|
||||
::: {.figure #fig-staging-workflow}
|
||||
# Staging workflow
|
||||
<!-- generated from ./staging-workflow.dot using: dot -Tsvg staging-workflow.dot > staging-workflow.svg -->
|
||||

|
||||
:::
|
||||
### Branches {#submitting-changes-branches}
|
||||
|
||||
[This GitHub Action](https://github.com/NixOS/nixpkgs/blob/master/.github/workflows/periodic-merge-6h.yml) brings changes from `master` to `staging-next` and from `staging-next` to `staging` every 6 hours; these are the blue arrows in the diagram above. The purple arrows in the diagram above are done manually and much less frequently. You can get an idea of how often these merges occur by looking at the git history.
|
||||
The `nixpkgs` repository has three major branches:
|
||||
- `master`
|
||||
- `staging`
|
||||
- `staging-next`
|
||||
|
||||
The most important distinction between them is that `staging`
|
||||
(colored red in the diagram below) can receive commits which cause
|
||||
a mass-rebuild (for example, anything that changes the `drvPath` of
|
||||
`stdenv`). The other two branches `staging-next` and `master`
|
||||
(colored green in the diagram below) can *not* receive commits which
|
||||
cause a mass-rebuild.
|
||||
|
||||
Arcs between the branches show possible merges into these branches,
|
||||
either from other branches or from independently submitted PRs. The
|
||||
colors of these edges likewise show whether or not they could
|
||||
trigger a mass rebuild (red) or must not trigger a mass rebuild
|
||||
(green).
|
||||
|
||||
Hydra runs automatic builds for the green branches.
|
||||
|
||||
Notice that the automatic merges are all green arrows. This is by
|
||||
design. Any merge which might cause a mass rebuild on a branch
|
||||
which has automatic builds (`staging-next`, `master`) will be a
|
||||
manual merge to make sure it is good use of compute power.
|
||||
|
||||
Nixpkgs has two branches so that there is one branch (`staging`)
|
||||
which accepts mass-rebuilding commits, and one fast-rebuilding
|
||||
branch which accepts independent PRs (`master`). The `staging-next`
|
||||
branch allows the Hydra operators to batch groups of commits to
|
||||
`staging` to be built. By keeping the `staging-next` branch
|
||||
separate from `staging`, this batching does not block
|
||||
developers from merging changes into `staging`.
|
||||
|
||||
```{.graphviz caption="Staging workflow"}
|
||||
digraph {
|
||||
master [color="green" fontcolor=green]
|
||||
"staging-next" [color="green" fontcolor=green]
|
||||
staging [color="red" fontcolor=red]
|
||||
|
||||
"small changes" [fontcolor=green shape=none]
|
||||
"small changes" -> master [color=green]
|
||||
|
||||
"mass-rebuilds and other large changes" [fontcolor=red shape=none]
|
||||
"mass-rebuilds and other large changes" -> staging [color=red]
|
||||
|
||||
"critical security fixes" [fontcolor=green shape=none]
|
||||
"critical security fixes" -> master [color=green]
|
||||
|
||||
"staging fixes which do not cause staging to mass-rebuild" [fontcolor=green shape=none]
|
||||
"staging fixes which do not cause staging to mass-rebuild" -> "staging-next" [color=green]
|
||||
|
||||
"staging-next" -> master [color="red"] [label="manual merge"] [fontcolor="red"]
|
||||
"staging" -> "staging-next" [color="red"] [label="manual merge"] [fontcolor="red"]
|
||||
|
||||
master -> "staging-next" [color="green"] [label="automatic merge (GitHub Action)"] [fontcolor="green"]
|
||||
"staging-next" -> staging [color="green"] [label="automatic merge (GitHub Action)"] [fontcolor="green"]
|
||||
}
|
||||
```
|
||||
|
||||
[This GitHub Action](https://github.com/NixOS/nixpkgs/blob/master/.github/workflows/periodic-merge-6h.yml) brings changes from `master` to `staging-next` and from `staging-next` to `staging` every 6 hours; these are the green arrows in the diagram above. The red arrows in the diagram above are done manually and much less frequently. You can get an idea of how often these merges occur by looking at the git history.
|
||||
|
||||
|
||||
### Master branch {#submitting-changes-master-branch}
|
||||
#### Master branch {#submitting-changes-master-branch}
|
||||
|
||||
The `master` branch is the main development branch. It should only see non-breaking commits that do not cause mass rebuilds.
|
||||
|
||||
### Staging branch {#submitting-changes-staging-branch}
|
||||
#### Staging branch {#submitting-changes-staging-branch}
|
||||
|
||||
The `staging` branch is a development branch where mass-rebuilds go. Mass rebuilds are commits that cause rebuilds for many packages, like more than 500 (or perhaps, if it's 'light' packages, 1000). It should only see non-breaking mass-rebuild commits. That means it is not to be used for testing, and changes must have been well tested already. If the branch is already in a broken state, please refrain from adding extra new breakages.
|
||||
|
||||
During the process of a releasing a new NixOS version, this branch or the release-critical packages can be restricted to non-breaking changes.
|
||||
|
||||
### Staging-next branch {#submitting-changes-staging-next-branch}
|
||||
#### Staging-next branch {#submitting-changes-staging-next-branch}
|
||||
|
||||
The `staging-next` branch is for stabilizing mass-rebuilds submitted to the `staging` branch prior to merging them into `master`. Mass-rebuilds must go via the `staging` branch. It must only see non-breaking commits that are fixing issues blocking it from being merged into the `master` branch.
|
||||
|
||||
|
@ -241,7 +296,7 @@ If the branch is already in a broken state, please refrain from adding extra new
|
|||
|
||||
During the process of a releasing a new NixOS version, this branch or the release-critical packages can be restricted to non-breaking changes.
|
||||
|
||||
### Stable release branches {#submitting-changes-stable-release-branches}
|
||||
#### Stable release branches {#submitting-changes-stable-release-branches}
|
||||
|
||||
The same staging workflow applies to stable release branches, but the main branch is called `release-*` instead of `master`.
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ let
|
|||
inherit (pkgs) lib;
|
||||
inherit (lib) hasPrefix removePrefix;
|
||||
|
||||
common = import ./common.nix;
|
||||
|
||||
lib-docs = import ./doc-support/lib-function-docs.nix {
|
||||
inherit pkgs nixpkgs;
|
||||
libsets = [
|
||||
|
@ -132,15 +134,15 @@ in pkgs.stdenv.mkDerivation {
|
|||
'';
|
||||
|
||||
installPhase = ''
|
||||
dest="$out/share/doc/nixpkgs"
|
||||
dest="$out/${common.outputPath}"
|
||||
mkdir -p "$(dirname "$dest")"
|
||||
mv out "$dest"
|
||||
mv "$dest/index.html" "$dest/manual.html"
|
||||
mv "$dest/index.html" "$dest/${common.indexPath}"
|
||||
|
||||
cp ${epub} "$dest/nixpkgs-manual.epub"
|
||||
|
||||
mkdir -p $out/nix-support/
|
||||
echo "doc manual $dest manual.html" >> $out/nix-support/hydra-build-products
|
||||
echo "doc manual $dest ${common.indexPath}" >> $out/nix-support/hydra-build-products
|
||||
echo "doc manual $dest nixpkgs-manual.epub" >> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
}
|
||||
|
|
|
@ -12,8 +12,11 @@ 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
|
||||
cudaSupport ? false
|
||||
cudaPackages ? {}
|
||||
{ config
|
||||
, cudaSupport ? config.cudaSupport
|
||||
, cudaPackages ? { }
|
||||
, ...
|
||||
}:
|
||||
```
|
||||
|
||||
When using `callPackage`, you can choose to pass in a different variant, e.g.
|
||||
|
|
|
@ -20,7 +20,7 @@ In the following is an example expression using `buildGoModule`, the following a
|
|||
|
||||
To obtain the actual hash, set `vendorHash = lib.fakeSha256;` and run the build ([more details here](#sec-source-hashes)).
|
||||
- `proxyVendor`: Fetches (go mod download) and proxies the vendor directory. This is useful if your code depends on c code and go mod tidy does not include the needed sources to build or if any dependency has case-insensitive conflicts which will produce platform-dependent `vendorHash` checksums.
|
||||
- `modPostBuild`: Shell commands to run after the build of the go-modules executes `go mod vendor`, and before calculating fixed output derivation's `vendorHash` (or `vendorSha256`). Note that if you change this attribute, you need to update `vendorHash` (or `vendorSha256`) attribute.
|
||||
- `modPostBuild`: Shell commands to run after the build of the goModules executes `go mod vendor`, and before calculating fixed output derivation's `vendorHash` (or `vendorSha256`). Note that if you change this attribute, you need to update `vendorHash` (or `vendorSha256`) attribute.
|
||||
|
||||
```nix
|
||||
pet = buildGoModule rec {
|
||||
|
@ -115,7 +115,7 @@ done
|
|||
|
||||
## Attributes used by the builders {#ssec-go-common-attributes}
|
||||
|
||||
Many attributes [controlling the build phase](#variables-controlling-the-build-phase) are respected by both `buildGoModule` and `buildGoPackage`. Note that `buildGoModule` reads the following attributes also when building the `vendor/` go-modules fixed output derivation as well:
|
||||
Many attributes [controlling the build phase](#variables-controlling-the-build-phase) are respected by both `buildGoModule` and `buildGoPackage`. Note that `buildGoModule` reads the following attributes also when building the `vendor/` goModules fixed output derivation as well:
|
||||
|
||||
- [`sourceRoot`](#var-stdenv-sourceRoot)
|
||||
- [`prePatch`](#var-stdenv-prePatch)
|
||||
|
|
|
@ -45,16 +45,17 @@ The attribute names in `haskellPackages` always correspond with their name on
|
|||
Hackage. Since Hackage allows names that are not valid Nix without escaping,
|
||||
you need to take care when handling attribute names like `3dmodels`.
|
||||
|
||||
For packages that are part of [Stackage], we use the version prescribed by a
|
||||
Stackage solver (usually the current LTS one) as the default version. For all
|
||||
other packages we use the latest version from Hackage. See
|
||||
[below](#haskell-available-versions) to learn which versions are provided
|
||||
exactly.
|
||||
For packages that are part of [Stackage] (a curated set of known to be
|
||||
compatible packages), we use the version prescribed by a Stackage snapshot
|
||||
(usually the current LTS one) as the default version. For all other packages we
|
||||
use the latest version from [Hackage](https://hackage.org) (the repository of
|
||||
basically all open source Haskell packages). See [below](#haskell-available-
|
||||
versions) for a few more details on this.
|
||||
|
||||
Roughly half of the 16K packages contained in `haskellPackages` don't actually
|
||||
build and are marked as broken semi-automatically. Most of those packages are
|
||||
deprecated or unmaintained, but sometimes packages that should build, do not
|
||||
build. Very often fixing them is not a lot of work.
|
||||
Roughly half of the 16K packages contained in `haskellPackages` don’t actually
|
||||
build and are [marked as broken semi-automatically](https://github.com/NixOS/nixpkgs/blob/haskell-updates/pkgs/development/haskell-modules/configuration-hackage2nix/broken.yaml).
|
||||
Most of those packages are deprecated or unmaintained, but sometimes packages
|
||||
that should build, do not build. Very often fixing them is not a lot of work.
|
||||
|
||||
<!--
|
||||
TODO(@sternenseemann):
|
||||
|
@ -126,19 +127,23 @@ Every package set also re-exposes the GHC used to build its packages as `haskell
|
|||
### Available package versions {#haskell-available-versions}
|
||||
|
||||
We aim for a “blessed” package set which only contains one version of each
|
||||
package, like Stackage (and based on it) but with more packages. Normally in
|
||||
nixpkgs the number of building Haskell packages is roughly two to three times
|
||||
the size of Stackage. For choosing the version to use for a certain package we
|
||||
use the following rules:
|
||||
package, like [Stackage], which is a curated set of known to be compatible
|
||||
packages. We use the version information from Stackage snapshots and extend it
|
||||
with more packages. Normally in Nixpkgs the number of building Haskell packages
|
||||
is roughly two to three times the size of Stackage. For choosing the version to
|
||||
use for a certain package we use the following rules:
|
||||
|
||||
1. By default, for every package `haskellPackages.foo` is the newest version
|
||||
found on Hackage (at the time of the last update of our package set).
|
||||
2. If the Stackage snapshot that we use (usually the newest LTS snapshot)
|
||||
contains a package, we use the Stackage version as default version for that
|
||||
package.
|
||||
3. For some packages, which are not on Stackage, we have manual overrides to
|
||||
set the default version to a version older than the newest on Hackage. We do
|
||||
this to get them or their reverse dependencies to compile in our package set.
|
||||
1. By default, for `haskellPackages.foo` is the newest version of the package
|
||||
`foo` found on [Hackage](https://hackage.org), which is the central registry
|
||||
of all open source Haskell packages. Nixpkgs contains a reference to a pinned
|
||||
Hackage snapshot, thus we use the state of Hackage as of the last time we
|
||||
updated this pin.
|
||||
2. If the [Stackage] snapshot that we use (usually the newest LTS snapshot)
|
||||
contains a package, [we use instead the version in the Stackage snapshot as
|
||||
default version for that package.](https://github.com/NixOS/nixpkgs/blob/haskell-updates/pkgs/development/haskell-modules/configuration-hackage2nix/stackage.yaml)
|
||||
3. For some packages, which are not on Stackage, we have if necessary [manual
|
||||
overrides to set the default version to a version older than the newest on
|
||||
Hackage.](https://github.com/NixOS/nixpkgs/blob/haskell-updates/pkgs/development/haskell-modules/configuration-hackage2nix/main.yaml)
|
||||
4. For all packages, for which the newest Hackage version is not the default
|
||||
version, there will also be a `haskellPackages.foo_x_y_z` package with the
|
||||
newest version. The `x_y_z` part encodes the version with dots replaced by
|
||||
|
@ -146,9 +151,12 @@ underscores. When the newest version changes by a new release to Hackage the
|
|||
old package will disappear under that name and be replaced by a newer one under
|
||||
the name with the new version. The package name including the version will
|
||||
also disappear when the default version e.g. from Stackage catches up with the
|
||||
newest version from Hackage.
|
||||
5. For some packages, we also manually add other `haskellPackages.foo_x_y_z`
|
||||
versions, if they are required for a certain build.
|
||||
newest version from Hackage. E.g. if `haskellPackages.foo` gets updated from
|
||||
1.0.0 to 1.1.0 the package `haskellPackages.foo_1_1_0` becomes obsolete and
|
||||
gets dropped.
|
||||
5. For some packages, we also [manually add other `haskellPackages.foo_x_y_z`
|
||||
versions](https://github.com/NixOS/nixpkgs/blob/haskell-updates/pkgs/development/haskell-modules/configuration-hackage2nix/main.yaml),
|
||||
if they are required for a certain build.
|
||||
|
||||
Relying on `haskellPackages.foo_x_y_z` attributes in derivations outside
|
||||
nixpkgs is discouraged because they may change or disappear with every package
|
||||
|
|
|
@ -1514,11 +1514,11 @@ Note: There is a boolean value `lib.inNixShell` set to `true` if nix-shell is in
|
|||
|
||||
### Tools {#tools}
|
||||
|
||||
Packages inside nixpkgs are written by hand. However many tools exist in
|
||||
community to help save time. No tool is preferred at the moment.
|
||||
Packages inside nixpkgs must use the `buildPythonPackage` or `buildPythonApplication` function directly,
|
||||
because we can only provide security support for non-vendored dependencies.
|
||||
|
||||
- [nixpkgs-pytools](https://github.com/nix-community/nixpkgs-pytools)
|
||||
- [poetry2nix](https://github.com/nix-community/poetry2nix)
|
||||
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`.
|
||||
|
||||
### Deterministic builds {#deterministic-builds}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ rustPlatform.buildRustPackage rec {
|
|||
description = "A fast line-oriented regex search tool, similar to ag and ack";
|
||||
homepage = "https://github.com/BurntSushi/ripgrep";
|
||||
license = licenses.unlicense;
|
||||
maintainers = [ maintainers.tailhook ];
|
||||
maintainers = [];
|
||||
};
|
||||
}
|
||||
```
|
||||
|
@ -926,7 +926,7 @@ rustPlatform.buildRustPackage rec {
|
|||
description = "A fast line-oriented regex search tool, similar to ag and ack";
|
||||
homepage = "https://github.com/BurntSushi/ripgrep";
|
||||
license = with licenses; [ mit unlicense ];
|
||||
maintainers = with maintainers; [ tailhook ];
|
||||
maintainers = with maintainers; [];
|
||||
};
|
||||
}
|
||||
```
|
||||
|
|
20
doc/shell.nix
Normal file
20
doc/shell.nix
Normal file
|
@ -0,0 +1,20 @@
|
|||
let
|
||||
pkgs = import ../. {
|
||||
config = {};
|
||||
overlays = [];
|
||||
};
|
||||
|
||||
common = import ./common.nix;
|
||||
inherit (common) outputPath indexPath;
|
||||
|
||||
web-devmode = import ../pkgs/tools/nix/web-devmode.nix {
|
||||
inherit pkgs;
|
||||
buildArgs = "./.";
|
||||
open = "/${outputPath}/${indexPath}";
|
||||
};
|
||||
in
|
||||
pkgs.mkShell {
|
||||
packages = [
|
||||
web-devmode
|
||||
];
|
||||
}
|
73
lib/README.md
Normal file
73
lib/README.md
Normal file
|
@ -0,0 +1,73 @@
|
|||
# Nixpkgs lib
|
||||
|
||||
This directory contains the implementation, documentation and tests for the Nixpkgs `lib` library.
|
||||
|
||||
## Overview
|
||||
|
||||
The evaluation entry point for `lib` is [`default.nix`](default.nix).
|
||||
This file evaluates to an attribute set containing two separate kinds of attributes:
|
||||
- Sub-libraries:
|
||||
Attribute sets grouping together similar functionality.
|
||||
Each sub-library is defined in a separate file usually matching its attribute name.
|
||||
|
||||
Example: `lib.lists` is a sub-library containing list-related functionality such as `lib.lists.take` and `lib.lists.imap0`.
|
||||
These are defined in the file [`lists.nix`](lists.nix).
|
||||
|
||||
- Aliases:
|
||||
Attributes that point to an attribute of the same name in some sub-library.
|
||||
|
||||
Example: `lib.take` is an alias for `lib.lists.take`.
|
||||
|
||||
Most files in this directory are definitions of sub-libraries, but there are a few others:
|
||||
- [`minver.nix`](minver.nix): A string of the minimum version of Nix that is required to evaluate Nixpkgs.
|
||||
- [`tests`](tests): Tests, see [Running tests](#running-tests)
|
||||
- [`release.nix`](tests/release.nix): A derivation aggregating all tests
|
||||
- [`misc.nix`](tests/misc.nix): Evaluation unit tests for most sub-libraries
|
||||
- `*.sh`: Bash scripts that run tests for specific sub-libraries
|
||||
- All other files in this directory exist to support the tests
|
||||
- [`systems`](systems): The `lib.systems` sub-library, structured into a directory instead of a file due to its complexity
|
||||
- [`path`](path): The `lib.path` sub-library, which includes tests as well as a document describing the design goals of `lib.path`
|
||||
- All other files in this directory are sub-libraries
|
||||
|
||||
### Module system
|
||||
|
||||
The [module system](https://nixos.org/manual/nixpkgs/#module-system) spans multiple sub-libraries:
|
||||
- [`modules.nix`](modules.nix): `lib.modules` for the core functions and anything not relating to option definitions
|
||||
- [`options.nix`](options.nix): `lib.options` for anything relating to option definitions
|
||||
- [`types.nix`](types.nix): `lib.types` for module system types
|
||||
|
||||
## Reference documentation
|
||||
|
||||
Reference documentation for library functions is written above each function as a multi-line comment.
|
||||
These comments are processed using [nixdoc](https://github.com/nix-community/nixdoc) and [rendered in the Nixpkgs manual](https://nixos.org/manual/nixpkgs/stable/#chap-functions).
|
||||
The nixdoc README describes the [comment format](https://github.com/nix-community/nixdoc#comment-format).
|
||||
|
||||
See the [chapter on contributing to the Nixpkgs manual](https://nixos.org/manual/nixpkgs/#chap-contributing) for how to build the manual.
|
||||
|
||||
## Running tests
|
||||
|
||||
All library tests can be run by building the derivation in [`tests/release.nix`](tests/release.nix):
|
||||
|
||||
```bash
|
||||
nix-build tests/release.nix
|
||||
```
|
||||
|
||||
Some commands for quicker iteration over parts of the test suite are also available:
|
||||
|
||||
```bash
|
||||
# Run all evaluation unit tests in tests/misc.nix
|
||||
# if the resulting list is empty, all tests passed
|
||||
nix-instantiate --eval --strict tests/misc.nix
|
||||
|
||||
# Run the module system tests
|
||||
tests/modules.sh
|
||||
|
||||
# Run the lib.sources tests
|
||||
tests/sources.sh
|
||||
|
||||
# Run the lib.filesystem tests
|
||||
tests/filesystem.sh
|
||||
|
||||
# Run the lib.path property tests
|
||||
path/tests/prop.sh
|
||||
```
|
|
@ -738,6 +738,42 @@ rec {
|
|||
sets:
|
||||
zipAttrsWith (name: values: values) sets;
|
||||
|
||||
/*
|
||||
Merge a list of attribute sets together using the `//` operator.
|
||||
In case of duplicate attributes, values from later list elements take precedence over earlier ones.
|
||||
The result is the same as `foldl mergeAttrs { }`, but the performance is better for large inputs.
|
||||
For n list elements, each with an attribute set containing m unique attributes, the complexity of this operation is O(nm log n).
|
||||
|
||||
Type:
|
||||
mergeAttrsList :: [ Attrs ] -> Attrs
|
||||
|
||||
Example:
|
||||
mergeAttrsList [ { a = 0; b = 1; } { c = 2; d = 3; } ]
|
||||
=> { a = 0; b = 1; c = 2; d = 3; }
|
||||
mergeAttrsList [ { a = 0; } { a = 1; } ]
|
||||
=> { a = 1; }
|
||||
*/
|
||||
mergeAttrsList = list:
|
||||
let
|
||||
# `binaryMerge start end` merges the elements at indices `index` of `list` such that `start <= index < end`
|
||||
# Type: Int -> Int -> Attrs
|
||||
binaryMerge = start: end:
|
||||
# assert start < end; # Invariant
|
||||
if end - start >= 2 then
|
||||
# If there's at least 2 elements, split the range in two, recurse on each part and merge the result
|
||||
# The invariant is satisfied because each half will have at least 1 element
|
||||
binaryMerge start (start + (end - start) / 2)
|
||||
// binaryMerge (start + (end - start) / 2) end
|
||||
else
|
||||
# Otherwise there will be exactly 1 element due to the invariant, in which case we just return it directly
|
||||
elemAt list start;
|
||||
in
|
||||
if list == [ ] then
|
||||
# Calling binaryMerge as below would not satisfy its invariant
|
||||
{ }
|
||||
else
|
||||
binaryMerge 0 (length list);
|
||||
|
||||
|
||||
/* Does the same as the update operator '//' except that attributes are
|
||||
merged until the given predicate is verified. The predicate should
|
||||
|
|
|
@ -81,9 +81,10 @@ rec {
|
|||
*/
|
||||
toKeyValue = {
|
||||
mkKeyValue ? mkKeyValueDefault {} "=",
|
||||
listsAsDuplicateKeys ? false
|
||||
listsAsDuplicateKeys ? false,
|
||||
indent ? ""
|
||||
}:
|
||||
let mkLine = k: v: mkKeyValue k v + "\n";
|
||||
let mkLine = k: v: indent + mkKeyValue k v + "\n";
|
||||
mkLines = if listsAsDuplicateKeys
|
||||
then k: v: map (mkLine k) (if lib.isList v then v else [v])
|
||||
else k: v: [ (mkLine k v) ];
|
||||
|
|
|
@ -657,6 +657,13 @@ in mkLicense lset) ({
|
|||
redistributable = true;
|
||||
};
|
||||
|
||||
hl3 = {
|
||||
fullName = "Hippocratic License v3.0";
|
||||
url = "https://firstdonoharm.dev/version/3/0/core.txt";
|
||||
free = false;
|
||||
redistributable = true;
|
||||
};
|
||||
|
||||
issl = {
|
||||
fullName = "Intel Simplified Software License";
|
||||
url = "https://software.intel.com/en-us/license/intel-simplified-software-license";
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{ lib }:
|
||||
let
|
||||
inherit (lib.strings) toInt;
|
||||
inherit (lib.trivial) compare min;
|
||||
inherit (lib.trivial) compare min id;
|
||||
inherit (lib.attrsets) mapAttrs;
|
||||
in
|
||||
rec {
|
||||
|
@ -180,18 +180,18 @@ rec {
|
|||
else if len != 1 then multiple
|
||||
else head found;
|
||||
|
||||
/* Find the first element in the list matching the specified
|
||||
/* Find the first index in the list matching the specified
|
||||
predicate or return `default` if no such element exists.
|
||||
|
||||
Type: findFirst :: (a -> bool) -> a -> [a] -> a
|
||||
Type: findFirstIndex :: (a -> Bool) -> b -> [a] -> (Int | b)
|
||||
|
||||
Example:
|
||||
findFirst (x: x > 3) 7 [ 1 6 4 ]
|
||||
=> 6
|
||||
findFirst (x: x > 9) 7 [ 1 6 4 ]
|
||||
=> 7
|
||||
findFirstIndex (x: x > 3) null [ 0 6 4 ]
|
||||
=> 1
|
||||
findFirstIndex (x: x > 9) null [ 0 6 4 ]
|
||||
=> null
|
||||
*/
|
||||
findFirst =
|
||||
findFirstIndex =
|
||||
# Predicate
|
||||
pred:
|
||||
# Default value to return
|
||||
|
@ -229,7 +229,33 @@ rec {
|
|||
if resultIndex < 0 then
|
||||
default
|
||||
else
|
||||
elemAt list resultIndex;
|
||||
resultIndex;
|
||||
|
||||
/* Find the first element in the list matching the specified
|
||||
predicate or return `default` if no such element exists.
|
||||
|
||||
Type: findFirst :: (a -> bool) -> a -> [a] -> a
|
||||
|
||||
Example:
|
||||
findFirst (x: x > 3) 7 [ 1 6 4 ]
|
||||
=> 6
|
||||
findFirst (x: x > 9) 7 [ 1 6 4 ]
|
||||
=> 7
|
||||
*/
|
||||
findFirst =
|
||||
# Predicate
|
||||
pred:
|
||||
# Default value to return
|
||||
default:
|
||||
# Input list
|
||||
list:
|
||||
let
|
||||
index = findFirstIndex pred null list;
|
||||
in
|
||||
if index == null then
|
||||
default
|
||||
else
|
||||
elemAt list index;
|
||||
|
||||
/* Return true if function `pred` returns true for at least one
|
||||
element of `list`.
|
||||
|
@ -637,6 +663,32 @@ rec {
|
|||
else if start + count > len then len - start
|
||||
else count);
|
||||
|
||||
/* The common prefix of two lists.
|
||||
|
||||
Type: commonPrefix :: [a] -> [a] -> [a]
|
||||
|
||||
Example:
|
||||
commonPrefix [ 1 2 3 4 5 6 ] [ 1 2 4 8 ]
|
||||
=> [ 1 2 ]
|
||||
commonPrefix [ 1 2 3 ] [ 1 2 3 4 5 ]
|
||||
=> [ 1 2 3 ]
|
||||
commonPrefix [ 1 2 3 ] [ 4 5 6 ]
|
||||
=> [ ]
|
||||
*/
|
||||
commonPrefix =
|
||||
list1:
|
||||
list2:
|
||||
let
|
||||
# Zip the lists together into a list of booleans whether each element matches
|
||||
matchings = zipListsWith (fst: snd: fst != snd) list1 list2;
|
||||
# Find the first index where the elements don't match,
|
||||
# which will then also be the length of the common prefix.
|
||||
# If all elements match, we fall back to the length of the zipped list,
|
||||
# which is the same as the length of the smaller list.
|
||||
commonPrefixLength = findFirstIndex id (length matchings) matchings;
|
||||
in
|
||||
take commonPrefixLength list1;
|
||||
|
||||
/* Return the last element of a list.
|
||||
|
||||
This function throws an error if the list is empty.
|
||||
|
|
115
lib/modules.nix
115
lib/modules.nix
|
@ -539,59 +539,74 @@ let
|
|||
|
||||
mergeModules' = prefix: options: configs:
|
||||
let
|
||||
/* byName is like foldAttrs, but will look for attributes to merge in the
|
||||
specified attribute name.
|
||||
|
||||
byName "foo" (module: value: ["module.hidden=${module.hidden},value=${value}"])
|
||||
[
|
||||
{
|
||||
hidden="baz";
|
||||
foo={qux="bar"; gla="flop";};
|
||||
}
|
||||
{
|
||||
hidden="fli";
|
||||
foo={qux="gne"; gli="flip";};
|
||||
}
|
||||
]
|
||||
===>
|
||||
{
|
||||
gla = [ "module.hidden=baz,value=flop" ];
|
||||
gli = [ "module.hidden=fli,value=flip" ];
|
||||
qux = [ "module.hidden=baz,value=bar" "module.hidden=fli,value=gne" ];
|
||||
}
|
||||
*/
|
||||
byName = attr: f: modules:
|
||||
zipAttrsWith (n: concatLists)
|
||||
(map (module: let subtree = module.${attr}; in
|
||||
# an attrset 'name' => list of submodules that declare ‘name’.
|
||||
declsByName =
|
||||
zipAttrsWith
|
||||
(n: concatLists)
|
||||
(map
|
||||
(module: let subtree = module.options; in
|
||||
if !(builtins.isAttrs subtree) then
|
||||
throw (if attr == "config" then ''
|
||||
You're trying to define a value of type `${builtins.typeOf subtree}'
|
||||
rather than an attribute set for the option
|
||||
`${builtins.concatStringsSep "." prefix}'!
|
||||
|
||||
This usually happens if `${builtins.concatStringsSep "." prefix}' has option
|
||||
definitions inside that are not matched. Please check how to properly define
|
||||
this option by e.g. referring to `man 5 configuration.nix'!
|
||||
'' else ''
|
||||
throw ''
|
||||
An option declaration for `${builtins.concatStringsSep "." prefix}' has type
|
||||
`${builtins.typeOf subtree}' rather than an attribute set.
|
||||
Did you mean to define this outside of `options'?
|
||||
'')
|
||||
''
|
||||
else
|
||||
mapAttrs (n: f module) subtree
|
||||
) modules);
|
||||
# an attrset 'name' => list of submodules that declare ‘name’.
|
||||
declsByName = byName "options" (module: option:
|
||||
[{ inherit (module) _file; options = option; }]
|
||||
) options;
|
||||
mapAttrs
|
||||
(n: option:
|
||||
[{ inherit (module) _file; options = option; }]
|
||||
)
|
||||
subtree
|
||||
)
|
||||
options);
|
||||
|
||||
# The root of any module definition must be an attrset.
|
||||
checkedConfigs =
|
||||
assert
|
||||
lib.all
|
||||
(c:
|
||||
# TODO: I have my doubts that this error would occur when option definitions are not matched.
|
||||
# The implementation of this check used to be tied to a superficially similar check for
|
||||
# options, so maybe that's why this is here.
|
||||
isAttrs c.config || throw ''
|
||||
In module `${c.file}', you're trying to define a value of type `${builtins.typeOf c.config}'
|
||||
rather than an attribute set for the option
|
||||
`${builtins.concatStringsSep "." prefix}'!
|
||||
|
||||
This usually happens if `${builtins.concatStringsSep "." prefix}' has option
|
||||
definitions inside that are not matched. Please check how to properly define
|
||||
this option by e.g. referring to `man 5 configuration.nix'!
|
||||
''
|
||||
)
|
||||
configs;
|
||||
configs;
|
||||
|
||||
# an attrset 'name' => list of submodules that define ‘name’.
|
||||
defnsByName = byName "config" (module: value:
|
||||
map (config: { inherit (module) file; inherit config; }) (pushDownProperties value)
|
||||
) configs;
|
||||
pushedDownDefinitionsByName =
|
||||
zipAttrsWith
|
||||
(n: concatLists)
|
||||
(map
|
||||
(module:
|
||||
mapAttrs
|
||||
(n: value:
|
||||
map (config: { inherit (module) file; inherit config; }) (pushDownProperties value)
|
||||
)
|
||||
module.config
|
||||
)
|
||||
checkedConfigs);
|
||||
# extract the definitions for each loc
|
||||
defnsByName' = byName "config" (module: value:
|
||||
[{ inherit (module) file; inherit value; }]
|
||||
) configs;
|
||||
rawDefinitionsByName =
|
||||
zipAttrsWith
|
||||
(n: concatLists)
|
||||
(map
|
||||
(module:
|
||||
mapAttrs
|
||||
(n: value:
|
||||
[{ inherit (module) file; inherit value; }]
|
||||
)
|
||||
module.config
|
||||
)
|
||||
checkedConfigs);
|
||||
|
||||
# Convert an option tree decl to a submodule option decl
|
||||
optionTreeToOption = decl:
|
||||
|
@ -613,8 +628,8 @@ let
|
|||
# We're descending into attribute ‘name’.
|
||||
let
|
||||
loc = prefix ++ [name];
|
||||
defns = defnsByName.${name} or [];
|
||||
defns' = defnsByName'.${name} or [];
|
||||
defns = pushedDownDefinitionsByName.${name} or [];
|
||||
defns' = rawDefinitionsByName.${name} or [];
|
||||
optionDecls = filter (m: isOption m.options) decls;
|
||||
in
|
||||
if length optionDecls == length decls then
|
||||
|
@ -624,7 +639,7 @@ let
|
|||
unmatchedDefns = [];
|
||||
}
|
||||
else if optionDecls != [] then
|
||||
if all (x: x.options.type.name == "submodule") optionDecls
|
||||
if all (x: x.options.type.name or null == "submodule") optionDecls
|
||||
# Raw options can only be merged into submodules. Merging into
|
||||
# attrsets might be nice, but ambiguous. Suppose we have
|
||||
# attrset as a `attrsOf submodule`. User declares option
|
||||
|
@ -657,7 +672,7 @@ let
|
|||
# Propagate all unmatched definitions from nested option sets
|
||||
mapAttrs (n: v: v.unmatchedDefns) resultsByName
|
||||
# Plus the definitions for the current prefix that don't have a matching option
|
||||
// removeAttrs defnsByName' (attrNames matchedOptions);
|
||||
// removeAttrs rawDefinitionsByName (attrNames matchedOptions);
|
||||
in {
|
||||
inherit matchedOptions;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ let
|
|||
concatMap
|
||||
foldl'
|
||||
take
|
||||
drop
|
||||
;
|
||||
|
||||
inherit (lib.strings)
|
||||
|
@ -217,9 +218,110 @@ in /* No rec! Add dependencies on this file at the top. */ {
|
|||
second argument: "${toString path2}" with root "${toString path2Deconstructed.root}"'';
|
||||
take (length path1Deconstructed.components) path2Deconstructed.components == path1Deconstructed.components;
|
||||
|
||||
/*
|
||||
Remove the first path as a component-wise prefix from the second path.
|
||||
The result is a normalised subpath string, see `lib.path.subpath.normalise`.
|
||||
|
||||
Laws:
|
||||
|
||||
- Inverts `append` for normalised subpaths:
|
||||
|
||||
removePrefix p (append p s) == subpath.normalise s
|
||||
|
||||
Type:
|
||||
removePrefix :: Path -> Path -> String
|
||||
|
||||
Example:
|
||||
removePrefix /foo /foo/bar/baz
|
||||
=> "./bar/baz"
|
||||
removePrefix /foo /foo
|
||||
=> "./."
|
||||
removePrefix /foo/bar /foo
|
||||
=> <error>
|
||||
removePrefix /. /foo
|
||||
=> "./foo"
|
||||
*/
|
||||
removePrefix =
|
||||
path1:
|
||||
assert assertMsg
|
||||
(isPath path1)
|
||||
"lib.path.removePrefix: First argument is of type ${typeOf path1}, but a path was expected.";
|
||||
let
|
||||
path1Deconstructed = deconstructPath path1;
|
||||
path1Length = length path1Deconstructed.components;
|
||||
in
|
||||
path2:
|
||||
assert assertMsg
|
||||
(isPath path2)
|
||||
"lib.path.removePrefix: Second argument is of type ${typeOf path2}, but a path was expected.";
|
||||
let
|
||||
path2Deconstructed = deconstructPath path2;
|
||||
success = take path1Length path2Deconstructed.components == path1Deconstructed.components;
|
||||
components =
|
||||
if success then
|
||||
drop path1Length path2Deconstructed.components
|
||||
else
|
||||
throw ''
|
||||
lib.path.removePrefix: The first path argument "${toString path1}" is not a component-wise prefix of the second path argument "${toString path2}".'';
|
||||
in
|
||||
assert assertMsg
|
||||
(path1Deconstructed.root == path2Deconstructed.root) ''
|
||||
lib.path.removePrefix: Filesystem roots must be the same for both paths, but paths with different roots were given:
|
||||
first argument: "${toString path1}" with root "${toString path1Deconstructed.root}"
|
||||
second argument: "${toString path2}" with root "${toString path2Deconstructed.root}"'';
|
||||
joinRelPath components;
|
||||
|
||||
/*
|
||||
Split the filesystem root from a [path](https://nixos.org/manual/nix/stable/language/values.html#type-path).
|
||||
The result is an attribute set with these attributes:
|
||||
- `root`: The filesystem root of the path, meaning that this directory has no parent directory.
|
||||
- `subpath`: The [normalised subpath string](#function-library-lib.path.subpath.normalise) that when [appended](#function-library-lib.path.append) to `root` returns the original path.
|
||||
|
||||
Laws:
|
||||
- [Appending](#function-library-lib.path.append) the `root` and `subpath` gives the original path:
|
||||
|
||||
p ==
|
||||
append
|
||||
(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:
|
||||
|
||||
dirOf (splitRoot p).root == (splitRoot p).root
|
||||
|
||||
Type:
|
||||
splitRoot :: Path -> { root :: Path, subpath :: String }
|
||||
|
||||
Example:
|
||||
splitRoot /foo/bar
|
||||
=> { root = /.; subpath = "./foo/bar"; }
|
||||
|
||||
splitRoot /.
|
||||
=> { root = /.; subpath = "./."; }
|
||||
|
||||
# Nix neutralises `..` path components for all path values automatically
|
||||
splitRoot /foo/../bar
|
||||
=> { root = /.; subpath = "./bar"; }
|
||||
|
||||
splitRoot "/foo/bar"
|
||||
=> <error>
|
||||
*/
|
||||
splitRoot = path:
|
||||
assert assertMsg
|
||||
(isPath path)
|
||||
"lib.path.splitRoot: Argument is of type ${typeOf path}, but a path was expected";
|
||||
let
|
||||
deconstructed = deconstructPath path;
|
||||
in {
|
||||
root = deconstructed.root;
|
||||
subpath = joinRelPath deconstructed.components;
|
||||
};
|
||||
|
||||
/* Whether a value is a valid subpath string.
|
||||
|
||||
A subpath string points to a specific file or directory within an absolute base directory.
|
||||
It is a stricter form of a relative path that excludes `..` components, since those could escape the base directory.
|
||||
|
||||
- The value is a string
|
||||
|
||||
- The string is not empty
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Property tests for the `lib.path` library
|
||||
#
|
||||
# Property tests for lib/path/default.nix
|
||||
# It generates random path-like strings and runs the functions on
|
||||
# them, checking that the expected laws of the functions hold
|
||||
# Run:
|
||||
# [nixpkgs]$ lib/path/tests/prop.sh
|
||||
# or:
|
||||
# [nixpkgs]$ nix-build lib/tests/release.nix
|
||||
|
||||
set -euo pipefail
|
||||
shopt -s inherit_errexit
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{ libpath }:
|
||||
let
|
||||
lib = import libpath;
|
||||
inherit (lib.path) hasPrefix append subpath;
|
||||
inherit (lib.path) hasPrefix removePrefix append splitRoot subpath;
|
||||
|
||||
cases = lib.runTests {
|
||||
# Test examples from the lib.path.append documentation
|
||||
|
@ -57,6 +57,40 @@ let
|
|||
expected = true;
|
||||
};
|
||||
|
||||
testRemovePrefixExample1 = {
|
||||
expr = removePrefix /foo /foo/bar/baz;
|
||||
expected = "./bar/baz";
|
||||
};
|
||||
testRemovePrefixExample2 = {
|
||||
expr = removePrefix /foo /foo;
|
||||
expected = "./.";
|
||||
};
|
||||
testRemovePrefixExample3 = {
|
||||
expr = (builtins.tryEval (removePrefix /foo/bar /foo)).success;
|
||||
expected = false;
|
||||
};
|
||||
testRemovePrefixExample4 = {
|
||||
expr = removePrefix /. /foo;
|
||||
expected = "./foo";
|
||||
};
|
||||
|
||||
testSplitRootExample1 = {
|
||||
expr = splitRoot /foo/bar;
|
||||
expected = { root = /.; subpath = "./foo/bar"; };
|
||||
};
|
||||
testSplitRootExample2 = {
|
||||
expr = splitRoot /.;
|
||||
expected = { root = /.; subpath = "./."; };
|
||||
};
|
||||
testSplitRootExample3 = {
|
||||
expr = splitRoot /foo/../bar;
|
||||
expected = { root = /.; subpath = "./bar"; };
|
||||
};
|
||||
testSplitRootExample4 = {
|
||||
expr = (builtins.tryEval (splitRoot "/foo/bar")).success;
|
||||
expected = false;
|
||||
};
|
||||
|
||||
# Test examples from the lib.path.subpath.isValid documentation
|
||||
testSubpathIsValidExample1 = {
|
||||
expr = subpath.isValid null;
|
||||
|
|
|
@ -85,17 +85,18 @@ rec {
|
|||
# is why we use the more obscure "bfd" and not "binutils" for this
|
||||
# choice.
|
||||
else "bfd";
|
||||
extensions = rec {
|
||||
extensions = lib.optionalAttrs final.hasSharedLibraries {
|
||||
sharedLibrary =
|
||||
/**/ if final.isDarwin then ".dylib"
|
||||
if final.isDarwin then ".dylib"
|
||||
else if final.isWindows then ".dll"
|
||||
else ".so";
|
||||
} // {
|
||||
staticLibrary =
|
||||
/**/ if final.isWindows then ".lib"
|
||||
else ".a";
|
||||
library =
|
||||
/**/ if final.isStatic then staticLibrary
|
||||
else sharedLibrary;
|
||||
/**/ if final.isStatic then final.extensions.staticLibrary
|
||||
else final.extensions.sharedLibrary;
|
||||
executable =
|
||||
/**/ if final.isWindows then ".exe"
|
||||
else "";
|
||||
|
@ -132,6 +133,25 @@ rec {
|
|||
# uname -r
|
||||
release = null;
|
||||
};
|
||||
|
||||
# It is important that hasSharedLibraries==false when the platform has no
|
||||
# dynamic library loader. Various tools (including the gcc build system)
|
||||
# have knowledge of which platforms are incapable of dynamic linking, and
|
||||
# will still build on/for those platforms with --enable-shared, but simply
|
||||
# omit any `.so` build products such as libgcc_s.so. When that happens,
|
||||
# it causes hard-to-troubleshoot build failures.
|
||||
hasSharedLibraries = with final;
|
||||
(isAndroid || isGnu || isMusl # Linux (allows multiple libcs)
|
||||
|| isDarwin || isSunOS || isOpenBSD || isFreeBSD || isNetBSD # BSDs
|
||||
|| isCygwin || isMinGW # Windows
|
||||
) && !isStatic;
|
||||
|
||||
# The difference between `isStatic` and `hasSharedLibraries` is mainly the
|
||||
# addition of the `staticMarker` (see make-derivation.nix). Some
|
||||
# platforms, like embedded machines without a libc (e.g. arm-none-eabi)
|
||||
# don't support dynamic linking, but don't get the `staticMarker`.
|
||||
# `pkgsStatic` sets `isStatic=true`, so `pkgsStatic.hostPlatform` always
|
||||
# has the `staticMarker`.
|
||||
isStatic = final.isWasm || final.isRedox;
|
||||
|
||||
# Just a guess, based on `system`
|
||||
|
|
|
@ -37,6 +37,10 @@ rec {
|
|||
config = "armv6l-unknown-linux-gnueabihf";
|
||||
} // platforms.raspberrypi;
|
||||
|
||||
bluefield2 = {
|
||||
config = "aarch64-unknown-linux-gnu";
|
||||
} // platforms.bluefield2;
|
||||
|
||||
remarkable1 = {
|
||||
config = "armv7l-unknown-linux-gnueabihf";
|
||||
} // platforms.zero-gravitas;
|
||||
|
|
|
@ -209,6 +209,13 @@ rec {
|
|||
# Legacy attribute, for compatibility with existing configs only.
|
||||
raspberrypi2 = armv7l-hf-multiplatform;
|
||||
|
||||
# Nvidia Bluefield 2 (w. crypto support)
|
||||
bluefield2 = {
|
||||
gcc = {
|
||||
arch = "armv8-a+fp+simd+crc+crypto";
|
||||
};
|
||||
};
|
||||
|
||||
zero-gravitas = {
|
||||
linux-kernel = {
|
||||
name = "zero-gravitas";
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
# to run these tests:
|
||||
# nix-instantiate --eval --strict nixpkgs/lib/tests/misc.nix
|
||||
# if the resulting list is empty, all tests passed
|
||||
/*
|
||||
Nix evaluation tests for various lib functions.
|
||||
|
||||
Since these tests are implemented with Nix evaluation, error checking is limited to what `builtins.tryEval` can detect, which is `throw`'s and `abort`'s, without error messages.
|
||||
If you need to test error messages or more complex evaluations, see ./modules.sh, ./sources.sh or ./filesystem.sh as examples.
|
||||
|
||||
To run these tests:
|
||||
|
||||
[nixpkgs]$ nix-instantiate --eval --strict lib/tests/misc.nix
|
||||
|
||||
If the resulting list is empty, all tests passed.
|
||||
Alternatively, to run all `lib` tests:
|
||||
|
||||
[nixpkgs]$ nix-build lib/tests/release.nix
|
||||
*/
|
||||
with import ../default.nix;
|
||||
|
||||
let
|
||||
|
@ -488,6 +500,39 @@ runTests {
|
|||
expected = { a = [ 2 3 ]; b = [7]; c = [8];};
|
||||
};
|
||||
|
||||
testListCommonPrefixExample1 = {
|
||||
expr = lists.commonPrefix [ 1 2 3 4 5 6 ] [ 1 2 4 8 ];
|
||||
expected = [ 1 2 ];
|
||||
};
|
||||
testListCommonPrefixExample2 = {
|
||||
expr = lists.commonPrefix [ 1 2 3 ] [ 1 2 3 4 5 ];
|
||||
expected = [ 1 2 3 ];
|
||||
};
|
||||
testListCommonPrefixExample3 = {
|
||||
expr = lists.commonPrefix [ 1 2 3 ] [ 4 5 6 ];
|
||||
expected = [ ];
|
||||
};
|
||||
testListCommonPrefixEmpty = {
|
||||
expr = lists.commonPrefix [ ] [ 1 2 3 ];
|
||||
expected = [ ];
|
||||
};
|
||||
testListCommonPrefixSame = {
|
||||
expr = lists.commonPrefix [ 1 2 3 ] [ 1 2 3 ];
|
||||
expected = [ 1 2 3 ];
|
||||
};
|
||||
testListCommonPrefixLazy = {
|
||||
expr = lists.commonPrefix [ 1 ] [ 1 (abort "lib.lists.commonPrefix shouldn't evaluate this")];
|
||||
expected = [ 1 ];
|
||||
};
|
||||
# This would stack overflow if `commonPrefix` were implemented using recursion
|
||||
testListCommonPrefixLong =
|
||||
let
|
||||
longList = genList (n: n) 100000;
|
||||
in {
|
||||
expr = lists.commonPrefix longList longList;
|
||||
expected = longList;
|
||||
};
|
||||
|
||||
testSort = {
|
||||
expr = sort builtins.lessThan [ 40 2 30 42 ];
|
||||
expected = [2 30 40 42];
|
||||
|
@ -518,45 +563,55 @@ runTests {
|
|||
expected = false;
|
||||
};
|
||||
|
||||
testFindFirstExample1 = {
|
||||
expr = findFirst (x: x > 3) 7 [ 1 6 4 ];
|
||||
expected = 6;
|
||||
testFindFirstIndexExample1 = {
|
||||
expr = lists.findFirstIndex (x: x > 3) (abort "index found, so a default must not be evaluated") [ 1 6 4 ];
|
||||
expected = 1;
|
||||
};
|
||||
|
||||
testFindFirstExample2 = {
|
||||
expr = findFirst (x: x > 9) 7 [ 1 6 4 ];
|
||||
expected = 7;
|
||||
testFindFirstIndexExample2 = {
|
||||
expr = lists.findFirstIndex (x: x > 9) "a very specific default" [ 1 6 4 ];
|
||||
expected = "a very specific default";
|
||||
};
|
||||
|
||||
testFindFirstEmpty = {
|
||||
expr = findFirst (abort "when the list is empty, the predicate is not needed") null [];
|
||||
testFindFirstIndexEmpty = {
|
||||
expr = lists.findFirstIndex (abort "when the list is empty, the predicate is not needed") null [];
|
||||
expected = null;
|
||||
};
|
||||
|
||||
testFindFirstSingleMatch = {
|
||||
expr = findFirst (x: x == 5) null [ 5 ];
|
||||
expected = 5;
|
||||
testFindFirstIndexSingleMatch = {
|
||||
expr = lists.findFirstIndex (x: x == 5) null [ 5 ];
|
||||
expected = 0;
|
||||
};
|
||||
|
||||
testFindFirstSingleDefault = {
|
||||
expr = findFirst (x: false) null [ (abort "if the predicate doesn't access the value, it must not be evaluated") ];
|
||||
testFindFirstIndexSingleDefault = {
|
||||
expr = lists.findFirstIndex (x: false) null [ (abort "if the predicate doesn't access the value, it must not be evaluated") ];
|
||||
expected = null;
|
||||
};
|
||||
|
||||
testFindFirstNone = {
|
||||
expr = builtins.tryEval (findFirst (x: x == 2) null [ 1 (throw "the last element must be evaluated when there's no match") ]);
|
||||
testFindFirstIndexNone = {
|
||||
expr = builtins.tryEval (lists.findFirstIndex (x: x == 2) null [ 1 (throw "the last element must be evaluated when there's no match") ]);
|
||||
expected = { success = false; value = false; };
|
||||
};
|
||||
|
||||
# Makes sure that the implementation doesn't cause a stack overflow
|
||||
testFindFirstBig = {
|
||||
expr = findFirst (x: x == 1000000) null (range 0 1000000);
|
||||
testFindFirstIndexBig = {
|
||||
expr = lists.findFirstIndex (x: x == 1000000) null (range 0 1000000);
|
||||
expected = 1000000;
|
||||
};
|
||||
|
||||
testFindFirstLazy = {
|
||||
expr = findFirst (x: x == 1) 7 [ 1 (abort "list elements after the match must not be evaluated") ];
|
||||
expected = 1;
|
||||
testFindFirstIndexLazy = {
|
||||
expr = lists.findFirstIndex (x: x == 1) null [ 1 (abort "list elements after the match must not be evaluated") ];
|
||||
expected = 0;
|
||||
};
|
||||
|
||||
testFindFirstExample1 = {
|
||||
expr = lists.findFirst (x: x > 3) 7 [ 1 6 4 ];
|
||||
expected = 6;
|
||||
};
|
||||
|
||||
testFindFirstExample2 = {
|
||||
expr = lists.findFirst (x: x > 9) 7 [ 1 6 4 ];
|
||||
expected = 7;
|
||||
};
|
||||
|
||||
# ATTRSETS
|
||||
|
@ -609,6 +664,31 @@ runTests {
|
|||
};
|
||||
};
|
||||
|
||||
|
||||
testMergeAttrsListExample1 = {
|
||||
expr = attrsets.mergeAttrsList [ { a = 0; b = 1; } { c = 2; d = 3; } ];
|
||||
expected = { a = 0; b = 1; c = 2; d = 3; };
|
||||
};
|
||||
testMergeAttrsListExample2 = {
|
||||
expr = attrsets.mergeAttrsList [ { a = 0; } { a = 1; } ];
|
||||
expected = { a = 1; };
|
||||
};
|
||||
testMergeAttrsListExampleMany =
|
||||
let
|
||||
list = genList (n:
|
||||
listToAttrs (genList (m:
|
||||
let
|
||||
# Integer divide n by two to create duplicate attributes
|
||||
str = "halfn${toString (n / 2)}m${toString m}";
|
||||
in
|
||||
nameValuePair str str
|
||||
) 100)
|
||||
) 100;
|
||||
in {
|
||||
expr = attrsets.mergeAttrsList list;
|
||||
expected = foldl' mergeAttrs { } list;
|
||||
};
|
||||
|
||||
# code from the example
|
||||
testRecursiveUpdateUntil = {
|
||||
expr = recursiveUpdateUntil (path: l: r: path == ["foo"]) {
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
|
||||
# This script is used to test that the module system is working as expected.
|
||||
# Executing it runs tests for `lib.modules`, `lib.options` and `lib.types`.
|
||||
# By default it test the version of nixpkgs which is defined in the NIX_PATH.
|
||||
#
|
||||
# Run:
|
||||
# [nixpkgs]$ lib/tests/modules.sh
|
||||
# or:
|
||||
# [nixpkgs]$ nix-build lib/tests/release.nix
|
||||
|
||||
set -o errexit -o noclobber -o nounset -o pipefail
|
||||
shopt -s failglob inherit_errexit
|
||||
|
@ -207,7 +213,7 @@ checkConfigOutput '^"foo"$' config.submodule.foo ./declare-submoduleWith-special
|
|||
## shorthandOnlyDefines config behaves as expected
|
||||
checkConfigOutput '^true$' config.submodule.config ./declare-submoduleWith-shorthand.nix ./define-submoduleWith-shorthand.nix
|
||||
checkConfigError 'is not of type `boolean' config.submodule.config ./declare-submoduleWith-shorthand.nix ./define-submoduleWith-noshorthand.nix
|
||||
checkConfigError "You're trying to define a value of type \`bool'\n\s*rather than an attribute set for the option" config.submodule.config ./declare-submoduleWith-noshorthand.nix ./define-submoduleWith-shorthand.nix
|
||||
checkConfigError "In module ..*define-submoduleWith-shorthand.nix., you're trying to define a value of type \`bool'\n\s*rather than an attribute set for the option" config.submodule.config ./declare-submoduleWith-noshorthand.nix ./define-submoduleWith-shorthand.nix
|
||||
checkConfigOutput '^true$' config.submodule.config ./declare-submoduleWith-noshorthand.nix ./define-submoduleWith-noshorthand.nix
|
||||
|
||||
## submoduleWith should merge all modules in one swoop
|
||||
|
@ -365,6 +371,9 @@ checkConfigError \
|
|||
config.set \
|
||||
./declare-set.nix ./declare-enable-nested.nix
|
||||
|
||||
# Check that that merging of option collisions doesn't depend on type being set
|
||||
checkConfigError 'The option .group..*would be a parent of the following options, but its type .<no description>. does not support nested options.\n\s*- option.s. with prefix .group.enable..*' config.group.enable ./merge-typeless-option.nix
|
||||
|
||||
# Test that types.optionType merges types correctly
|
||||
checkConfigOutput '^10$' config.theOption.int ./optionTypeMerging.nix
|
||||
checkConfigOutput '^"hello"$' config.theOption.str ./optionTypeMerging.nix
|
||||
|
|
25
lib/tests/modules/merge-typeless-option.nix
Normal file
25
lib/tests/modules/merge-typeless-option.nix
Normal file
|
@ -0,0 +1,25 @@
|
|||
{ lib, ... }:
|
||||
|
||||
let
|
||||
typeless =
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
options.group = lib.mkOption { };
|
||||
};
|
||||
childOfTypeless =
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
options.group.enable = lib.mkEnableOption "nothing";
|
||||
};
|
||||
in
|
||||
|
||||
{
|
||||
imports = [
|
||||
typeless
|
||||
childOfTypeless
|
||||
];
|
||||
|
||||
config.group.enable = false;
|
||||
}
|
|
@ -1,4 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Tests lib/sources.nix
|
||||
# Run:
|
||||
# [nixpkgs]$ lib/tests/sources.sh
|
||||
# or:
|
||||
# [nixpkgs]$ nix-build lib/tests/release.nix
|
||||
|
||||
set -euo pipefail
|
||||
shopt -s inherit_errexit
|
||||
|
||||
|
|
|
@ -581,6 +581,12 @@
|
|||
githubId = 1318982;
|
||||
name = "Anders Claesson";
|
||||
};
|
||||
akechishiro = {
|
||||
email = "akechishiro-aur+nixpkgs@lahfa.xyz";
|
||||
github = "AkechiShiro";
|
||||
githubId = 14914796;
|
||||
name = "Samy Lahfa";
|
||||
};
|
||||
a-kenji = {
|
||||
email = "aks.kenji@protonmail.com";
|
||||
github = "a-kenji";
|
||||
|
@ -1231,6 +1237,18 @@
|
|||
githubId = 914687;
|
||||
name = "Alexis Praga";
|
||||
};
|
||||
aprl = {
|
||||
email = "aprl@acab.dev";
|
||||
github = "cutestnekoaqua";
|
||||
githubId = 30842467;
|
||||
name = "April John";
|
||||
};
|
||||
aqrln = {
|
||||
email = "nix@aqrln.net";
|
||||
github = "aqrln";
|
||||
githubId = 4923335;
|
||||
name = "Alexey Orlenko";
|
||||
};
|
||||
ar1a = {
|
||||
email = "aria@ar1as.space";
|
||||
github = "ar1a";
|
||||
|
@ -1383,6 +1401,12 @@
|
|||
githubId = 37193992;
|
||||
name = "Arthur Teisseire";
|
||||
};
|
||||
arti5an = {
|
||||
email = "artis4n@outlook.com";
|
||||
github = "arti5an";
|
||||
githubId = 14922630;
|
||||
name = "Richard Smith";
|
||||
};
|
||||
artturin = {
|
||||
email = "artturin@artturin.com";
|
||||
matrix = "@artturin:matrix.org";
|
||||
|
@ -1493,6 +1517,13 @@
|
|||
fingerprint = "DD52 6BC7 767D BA28 16C0 95E5 6840 89CE 67EB B691";
|
||||
}];
|
||||
};
|
||||
atalii = {
|
||||
email = "taliauster@gmail.com";
|
||||
github = "atalii";
|
||||
githubId = 120901234;
|
||||
name = "tali auster";
|
||||
matrix = "@atalii:matrix.org";
|
||||
};
|
||||
ataraxiasjel = {
|
||||
email = "nix@ataraxiadev.com";
|
||||
github = "AtaraxiaSjel";
|
||||
|
@ -1685,6 +1716,13 @@
|
|||
fingerprint = "2688 0377 C31D 9E81 9BDF 83A8 C8C6 BDDB 3847 F72B";
|
||||
}];
|
||||
};
|
||||
azazak123 = {
|
||||
email = "azazaka2002@gmail.com";
|
||||
matrix = "@ne_dvoeshnik:matrix.org";
|
||||
name = "Volodymyr Antonov";
|
||||
github = "azazak123";
|
||||
githubId = 50211158;
|
||||
};
|
||||
azd325 = {
|
||||
email = "tim.kleinschmidt@gmail.com";
|
||||
github = "Azd325";
|
||||
|
@ -1724,6 +1762,12 @@
|
|||
fingerprint = "6FBC A462 4EAF C69C A7C4 98C1 F044 3098 48A0 7CAC";
|
||||
}];
|
||||
};
|
||||
babeuh = {
|
||||
name = "Raphael Le Goaller";
|
||||
email = "babeuh@rlglr.fr";
|
||||
github = "babeuh";
|
||||
githubId = 60193302;
|
||||
};
|
||||
bachp = {
|
||||
email = "pascal.bach@nextrem.ch";
|
||||
matrix = "@bachp:matrix.org";
|
||||
|
@ -2561,6 +2605,12 @@
|
|||
}
|
||||
];
|
||||
};
|
||||
CaitlinDavitt = {
|
||||
email = "CaitlinDavitt@gmail.com";
|
||||
github = "CaitlinDavitt";
|
||||
githubId = 48105979;
|
||||
name = "Caitlin Davitt";
|
||||
};
|
||||
calavera = {
|
||||
email = "david.calavera@gmail.com";
|
||||
github = "calavera";
|
||||
|
@ -2753,6 +2803,13 @@
|
|||
githubId = 64804;
|
||||
name = "Dennis Gosnell";
|
||||
};
|
||||
cdmistman = {
|
||||
name = "Colton Donnelly";
|
||||
email = "colton@donn.io";
|
||||
matrix = "@donnellycolton:matrix.org";
|
||||
github = "cdmistman";
|
||||
githubId = 23486351;
|
||||
};
|
||||
ceedubs = {
|
||||
email = "ceedubs@gmail.com";
|
||||
github = "ceedubs";
|
||||
|
@ -2845,6 +2902,13 @@
|
|||
githubId = 6608071;
|
||||
name = "Charles Huyghues-Despointes";
|
||||
};
|
||||
chayleaf = {
|
||||
email = "chayleaf-nix@pavluk.org";
|
||||
github = "chayleaf";
|
||||
githubId = 9590981;
|
||||
matrix = "@chayleaf:matrix.pavluk.org";
|
||||
name = "Anna Pavlyuk";
|
||||
};
|
||||
chekoopa = {
|
||||
email = "chekoopa@mail.ru";
|
||||
github = "chekoopa";
|
||||
|
@ -2896,6 +2960,12 @@
|
|||
githubId = 14790226;
|
||||
name = "Hubert Jasudowicz";
|
||||
};
|
||||
c-h-johnson = {
|
||||
name = "Charles Johnson";
|
||||
email = "charles@charlesjohnson.name";
|
||||
github = "c-h-johnson";
|
||||
githubId = 138403247;
|
||||
};
|
||||
chkno = {
|
||||
email = "scottworley@scottworley.com";
|
||||
github = "chkno";
|
||||
|
@ -4083,6 +4153,12 @@
|
|||
fingerprint = "1C4E F4FE 7F8E D8B7 1E88 CCDF BAB1 D15F B7B4 D4CE";
|
||||
}];
|
||||
};
|
||||
dgollings = {
|
||||
email = "daniel.gollings+nixpkgs@gmail.com";
|
||||
github = "dgollings";
|
||||
githubId = 2032823;
|
||||
name = "Daniel Gollings";
|
||||
};
|
||||
dgonyeo = {
|
||||
email = "derek@gonyeo.com";
|
||||
github = "dgonyeo";
|
||||
|
@ -4284,6 +4360,12 @@
|
|||
githubId = 10998835;
|
||||
name = "Doron Behar";
|
||||
};
|
||||
dotemup = {
|
||||
email = "dotemup.designs+nixpkgs@gmail.com";
|
||||
github = "dotemup";
|
||||
githubId = 11077277;
|
||||
name = "Dote";
|
||||
};
|
||||
dotlambda = {
|
||||
email = "rschuetz17@gmail.com";
|
||||
matrix = "@robert:funklause.de";
|
||||
|
@ -5171,6 +5253,12 @@
|
|||
fingerprint = "FC1D 3E4F CBCA 80DF E870 6397 C811 6E3A 0C1C A76A";
|
||||
}];
|
||||
};
|
||||
exploitoverload = {
|
||||
email = "nix@exploitoverload.com";
|
||||
github = "exploitoverload";
|
||||
githubId = 99678549;
|
||||
name = "Asier Armenteros";
|
||||
};
|
||||
extends = {
|
||||
email = "sharosari@gmail.com";
|
||||
github = "ImExtends";
|
||||
|
@ -5281,6 +5369,12 @@
|
|||
githubId = 4246921;
|
||||
name = "Florian Beeres";
|
||||
};
|
||||
fd = {
|
||||
email = "simon.menke@gmail.com";
|
||||
github = "fd";
|
||||
githubId = 591;
|
||||
name = "Simon Menke";
|
||||
};
|
||||
fdns = {
|
||||
email = "fdns02@gmail.com";
|
||||
github = "fdns";
|
||||
|
@ -5570,6 +5664,12 @@
|
|||
githubId = 84968;
|
||||
name = "Florian Paul Schmidt";
|
||||
};
|
||||
fptje = {
|
||||
email = "fpeijnenburg@gmail.com";
|
||||
github = "FPtje";
|
||||
githubId = 1202014;
|
||||
name = "Falco Peijnenburg";
|
||||
};
|
||||
fragamus = {
|
||||
email = "innovative.engineer@gmail.com";
|
||||
github = "fragamus";
|
||||
|
@ -5611,6 +5711,12 @@
|
|||
githubId = 609279;
|
||||
name = "Isaac Shapira";
|
||||
};
|
||||
freyacodes = {
|
||||
email = "freya@arbjerg.dev";
|
||||
github = "freyacodes";
|
||||
githubId = 2582617;
|
||||
name = "Freya Arbjerg";
|
||||
};
|
||||
fricklerhandwerk = {
|
||||
email = "valentin@fricklerhandwerk.de";
|
||||
github = "fricklerhandwerk";
|
||||
|
@ -5719,6 +5825,11 @@
|
|||
githubId = 17859309;
|
||||
name = "Fuzen";
|
||||
};
|
||||
fwc = {
|
||||
github = "fwc";
|
||||
githubId = 29337229;
|
||||
name = "mtths";
|
||||
};
|
||||
fxfactorial = {
|
||||
email = "edgar.factorial@gmail.com";
|
||||
github = "fxfactorial";
|
||||
|
@ -5974,6 +6085,15 @@
|
|||
githubId = 127353;
|
||||
name = "Geoffrey Huntley";
|
||||
};
|
||||
gigglesquid = {
|
||||
email = "jack.connors@protonmail.com";
|
||||
github = "gigglesquid";
|
||||
githubId = 3685154;
|
||||
name = "Jack connors";
|
||||
keys = [{
|
||||
fingerprint = "21DF 8034 B212 EDFF 9F19 9C19 F65B 7583 7ABF D019";
|
||||
}];
|
||||
};
|
||||
gila = {
|
||||
email = "jeffry.molanus@gmail.com";
|
||||
github = "gila";
|
||||
|
@ -6061,6 +6181,12 @@
|
|||
githubId = 25820499;
|
||||
name = "Roman Kretschmer";
|
||||
};
|
||||
goatchurchprime = {
|
||||
email = "julian@goatchurch.org.uk";
|
||||
github = "goatchurchprime";
|
||||
githubId = 677254;
|
||||
name = "Julian Todd";
|
||||
};
|
||||
gobidev = {
|
||||
email = "adrian.groh@t-online.de";
|
||||
github = "Gobidev";
|
||||
|
@ -7336,6 +7462,11 @@
|
|||
github = "jali-clarke";
|
||||
githubId = 17733984;
|
||||
};
|
||||
james-atkins = {
|
||||
name = "James Atkins";
|
||||
github = "james-atkins";
|
||||
githubId = 9221409;
|
||||
};
|
||||
jamiemagee = {
|
||||
email = "jamie.magee@gmail.com";
|
||||
github = "JamieMagee";
|
||||
|
@ -8077,6 +8208,12 @@
|
|||
email = "j.loos@posteo.net";
|
||||
githubId = 57965027;
|
||||
};
|
||||
josephst = {
|
||||
name = "Joseph Stahl";
|
||||
email = "hello@josephstahl.com";
|
||||
github = "josephst";
|
||||
githubId = 1269177;
|
||||
};
|
||||
joshniemela = {
|
||||
name = "Joshua Niemelä";
|
||||
email = "josh@jniemela.dk";
|
||||
|
@ -8963,6 +9100,12 @@
|
|||
githubId = 5759930;
|
||||
name = "Alexis Destrez";
|
||||
};
|
||||
krupkat = {
|
||||
github = "krupkat";
|
||||
githubId = 6817216;
|
||||
name = "Tomas Krupka";
|
||||
matrix = "@krupkat:matrix.org";
|
||||
};
|
||||
ktf = {
|
||||
email = "giulio.eulisse@cern.ch";
|
||||
github = "ktf";
|
||||
|
@ -9037,6 +9180,12 @@
|
|||
fingerprint = "5A9A 1C9B 2369 8049 3B48 CF5B 81A1 5409 4816 2372";
|
||||
}];
|
||||
};
|
||||
l0b0 = {
|
||||
email = "victor@engmark.name";
|
||||
github = "l0b0";
|
||||
githubId = 168301;
|
||||
name = "Victor Engmark";
|
||||
};
|
||||
l3af = {
|
||||
email = "L3afMeAlon3@gmail.com";
|
||||
matrix = "@L3afMe:matrix.org";
|
||||
|
@ -9716,6 +9865,15 @@
|
|||
githubId = 22085373;
|
||||
name = "Luis Hebendanz";
|
||||
};
|
||||
luisdaranda = {
|
||||
email = "luisdomingoaranda@gmail.com";
|
||||
github = "propet";
|
||||
githubId = 8515861;
|
||||
name = "Luis D. Aranda Sánchez";
|
||||
keys = [{
|
||||
fingerprint = "AB7C 81F4 9E07 CC64 F3E7 BC25 DCAC C6F4 AAFC C04E";
|
||||
}];
|
||||
};
|
||||
luisnquin = {
|
||||
email = "lpaandres2020@gmail.com";
|
||||
matrix = "@luisnquin:matrix.org";
|
||||
|
@ -9811,6 +9969,12 @@
|
|||
githubId = 782440;
|
||||
name = "Luna Nova";
|
||||
};
|
||||
lurkki = {
|
||||
email = "jussi.kuokkanen@protonmail.com";
|
||||
github = "Lurkki14";
|
||||
githubId = 44469719;
|
||||
name = "Jussi Kuokkanen";
|
||||
};
|
||||
lux = {
|
||||
email = "lux@lux.name";
|
||||
github = "luxzeitlos";
|
||||
|
@ -9922,6 +10086,16 @@
|
|||
githubId = 93990818;
|
||||
name = "Madoura";
|
||||
};
|
||||
maeve = {
|
||||
email = "mrey@mailbox.org";
|
||||
matrix = "@maeve:catgirl.cloud";
|
||||
github = "m-rey";
|
||||
githubId = 42996147;
|
||||
name = "Mæve";
|
||||
keys = [{
|
||||
fingerprint = "96C9 D086 CC9D 7BD7 EF24 80E2 9168 796A 1CC3 AEA2";
|
||||
}];
|
||||
};
|
||||
mafo = {
|
||||
email = "Marc.Fontaine@gmx.de";
|
||||
github = "MarcFontaine";
|
||||
|
@ -10005,6 +10179,11 @@
|
|||
githubId = 2914269;
|
||||
name = "Malo Bourgon";
|
||||
};
|
||||
malt3 = {
|
||||
github = "malt3";
|
||||
githubId = 1780588;
|
||||
name = "Malte Poll";
|
||||
};
|
||||
malte-v = {
|
||||
email = "nixpkgs@mal.tc";
|
||||
github = "malte-v";
|
||||
|
@ -10659,6 +10838,16 @@
|
|||
fingerprint = "8CE3 2906 516F C4D8 D373 308A E189 648A 55F5 9A9F";
|
||||
}];
|
||||
};
|
||||
mib = {
|
||||
name = "mib";
|
||||
email = "mib@kanp.ai";
|
||||
matrix = "@mib:kanp.ai";
|
||||
github = "mibmo";
|
||||
githubId = 87388017;
|
||||
keys = [{
|
||||
fingerprint = "AB0D C647 B2F7 86EB 045C 7EFE CF6E 67DE D6DC 1E3F";
|
||||
}];
|
||||
};
|
||||
mic92 = {
|
||||
email = "joerg@thalheim.io";
|
||||
matrix = "@mic92:nixos.dev";
|
||||
|
@ -10755,6 +10944,12 @@
|
|||
fingerprint = "FEF0 AE2D 5449 3482 5F06 40AA 186A 1EDA C5C6 3F83";
|
||||
}];
|
||||
};
|
||||
mig4ng = {
|
||||
email = "mig4ng@gmail.com";
|
||||
github = "mig4ng";
|
||||
githubId = 5817039;
|
||||
name = "Miguel Carneiro";
|
||||
};
|
||||
mightyiam = {
|
||||
email = "mightyiampresence@gmail.com";
|
||||
github = "mightyiam";
|
||||
|
@ -11241,6 +11436,12 @@
|
|||
name = "Maxim Schuwalow";
|
||||
email = "maxim.schuwalow@gmail.com";
|
||||
};
|
||||
mschwaig = {
|
||||
name = "Martin Schwaighofer";
|
||||
github = "mschwaig";
|
||||
githubId = 3856390;
|
||||
email = "mschwaig+nixpkgs@eml.cc";
|
||||
};
|
||||
msfjarvis = {
|
||||
github = "msfjarvis";
|
||||
githubId = 13348378;
|
||||
|
@ -12038,6 +12239,12 @@
|
|||
githubId = 2946283;
|
||||
name = "Brian Cohen";
|
||||
};
|
||||
nova-madeline = {
|
||||
matrix = "@nova:tchncs.de";
|
||||
github = "nova-r";
|
||||
githubId = 126072875;
|
||||
name = "nova madeline";
|
||||
};
|
||||
novenary = {
|
||||
email = "streetwalkermc@gmail.com";
|
||||
github = "9ary";
|
||||
|
@ -12376,6 +12583,12 @@
|
|||
githubId = 75299;
|
||||
name = "Malcolm Matalka";
|
||||
};
|
||||
orichter = {
|
||||
email = "richter-oliver@gmx.net";
|
||||
github = "RichterOliver";
|
||||
githubId = 135209509;
|
||||
name = "Oliver Richter";
|
||||
};
|
||||
orivej = {
|
||||
email = "orivej@gmx.fr";
|
||||
github = "orivej";
|
||||
|
@ -12968,6 +13181,12 @@
|
|||
githubId = 1830959;
|
||||
name = "Piper McCorkle";
|
||||
};
|
||||
piturnah = {
|
||||
email = "peterhebden6@gmail.com";
|
||||
github = "piturnah";
|
||||
githubId = 20472367;
|
||||
name = "Peter Hebden";
|
||||
};
|
||||
pjbarnoy = {
|
||||
email = "pjbarnoy@gmail.com";
|
||||
github = "pjbarnoy";
|
||||
|
@ -14102,6 +14321,12 @@
|
|||
githubId = 1069318;
|
||||
name = "Robin Lambertz";
|
||||
};
|
||||
robwalt = {
|
||||
email = "robwalter96@gmail.com";
|
||||
github = "robwalt";
|
||||
githubId = 26892280;
|
||||
name = "Robert Walter";
|
||||
};
|
||||
roconnor = {
|
||||
email = "roconnor@theorem.ca";
|
||||
github = "roconnor";
|
||||
|
@ -14339,6 +14564,12 @@
|
|||
githubId = 889991;
|
||||
name = "Ryan Artecona";
|
||||
};
|
||||
ryanccn = {
|
||||
email = "hello@ryanccn.dev";
|
||||
github = "ryanccn";
|
||||
githubId = 70191398;
|
||||
name = "Ryan Cao";
|
||||
};
|
||||
ryane = {
|
||||
email = "ryanesc@gmail.com";
|
||||
github = "ryane";
|
||||
|
@ -14393,6 +14624,12 @@
|
|||
githubId = 3280280;
|
||||
name = "Ryne Everett";
|
||||
};
|
||||
ryota-ka = {
|
||||
email = "ok@ryota-ka.me";
|
||||
github = "ryota-ka";
|
||||
githubId = 7309170;
|
||||
name = "Ryota Kameoka";
|
||||
};
|
||||
rytone = {
|
||||
email = "max@ryt.one";
|
||||
github = "rastertail";
|
||||
|
@ -14798,6 +15035,16 @@
|
|||
githubId = 4805746;
|
||||
name = "Sebastian Jordan";
|
||||
};
|
||||
septem9er = {
|
||||
name = "Septem9er";
|
||||
email = "develop@septem9er.de";
|
||||
matrix = "@septem9er:fairydust.space";
|
||||
github = "septem9er";
|
||||
githubId = 33379902;
|
||||
keys = [{
|
||||
fingerprint = "C408 07F9 8677 3D98 EFF3 0980 355A 9AFB FD8E AD33";
|
||||
}];
|
||||
};
|
||||
seqizz = {
|
||||
email = "seqizz@gmail.com";
|
||||
github = "seqizz";
|
||||
|
@ -15155,6 +15402,12 @@
|
|||
githubId = 74881555;
|
||||
name = "Fofanov Sergey";
|
||||
};
|
||||
sitaaax = {
|
||||
email = "johannes@kle1n.com";
|
||||
github = "SitAAAx";
|
||||
githubId = 74413170;
|
||||
name = "Johannes Klein";
|
||||
};
|
||||
sivteck = {
|
||||
email = "sivaram1992@gmail.com";
|
||||
github = "sivteck";
|
||||
|
@ -15395,6 +15648,12 @@
|
|||
githubId = 7669898;
|
||||
name = "Katharina Fey";
|
||||
};
|
||||
spalf = {
|
||||
email = "tom@tombarrett.xyz";
|
||||
name = "tom barrett";
|
||||
github = "70m6";
|
||||
githubId = 105207964;
|
||||
};
|
||||
spease = {
|
||||
email = "peasteven@gmail.com";
|
||||
github = "spease";
|
||||
|
@ -15426,6 +15685,12 @@
|
|||
githubId = 6391601;
|
||||
name = "Roger Mason";
|
||||
};
|
||||
sputn1ck = {
|
||||
email = "kon@kon.ninja";
|
||||
github = "sputn1ck";
|
||||
githubId = 8904314;
|
||||
name = "Konstantin Nick";
|
||||
};
|
||||
squalus = {
|
||||
email = "squalus@squalus.net";
|
||||
github = "squalus";
|
||||
|
@ -15462,6 +15727,13 @@
|
|||
githubId = 219362;
|
||||
name = "Sarah Brofeldt";
|
||||
};
|
||||
srid = {
|
||||
email = "srid@srid.ca";
|
||||
matrix = "@srid:matrix.org";
|
||||
github = "srid";
|
||||
githubId = 3998;
|
||||
name = "Sridhar Ratnakumar";
|
||||
};
|
||||
srounce = {
|
||||
name = "Samuel Rounce";
|
||||
email = "me@samuelrounce.co.uk";
|
||||
|
@ -15730,6 +16002,12 @@
|
|||
githubId = 16734772;
|
||||
name = "Sumner Evans";
|
||||
};
|
||||
sund3RRR = {
|
||||
email = "evenquantity@gmail.com";
|
||||
github = "sund3RRR";
|
||||
githubId = 73298492;
|
||||
name = "Mikhail Kiselev";
|
||||
};
|
||||
suominen = {
|
||||
email = "kimmo@suominen.com";
|
||||
github = "suominen";
|
||||
|
@ -16362,6 +16640,12 @@
|
|||
github = "thielema";
|
||||
githubId = 898989;
|
||||
};
|
||||
thillux = {
|
||||
name = "Markus Theil";
|
||||
email = "theil.markus@gmail.com";
|
||||
github = "thillux";
|
||||
githubId = 2171995;
|
||||
};
|
||||
thilobillerbeck = {
|
||||
name = "Thilo Billerbeck";
|
||||
email = "thilo.billerbeck@officerent.de";
|
||||
|
@ -16625,6 +16909,14 @@
|
|||
githubId = 8577941;
|
||||
name = "Kevin Rauscher";
|
||||
};
|
||||
tomasajt = {
|
||||
github = "TomaSajt";
|
||||
githubId = 62384384;
|
||||
name = "TomaSajt";
|
||||
keys = [{
|
||||
fingerprint = "8CA9 8016 F44D B717 5B44 6032 F011 163C 0501 22A1";
|
||||
}];
|
||||
};
|
||||
tomaskala = {
|
||||
email = "public+nixpkgs@tomaskala.com";
|
||||
github = "tomaskala";
|
||||
|
@ -16901,6 +17193,12 @@
|
|||
matrix = "@ty:tjll.net";
|
||||
name = "Tyler Langlois";
|
||||
};
|
||||
tymscar = {
|
||||
email = "oscar@tymscar.com";
|
||||
github = "tymscar";
|
||||
githubId = 3742502;
|
||||
name = "Oscar Molnar";
|
||||
};
|
||||
typetetris = {
|
||||
email = "ericwolf42@mail.com";
|
||||
github = "typetetris";
|
||||
|
@ -17249,7 +17547,7 @@
|
|||
githubId = 7953163;
|
||||
name = "Vika Shleina";
|
||||
keys = [{
|
||||
fingerprint = "B3C0 DA1A C18B 82E8 CA8B B1D1 4F62 CD07 CE64 796A";
|
||||
fingerprint = "5814 50EB 6E17 E715 7C63 E7F1 9879 8C3C 4D68 8D6D";
|
||||
}];
|
||||
};
|
||||
vincentbernat = {
|
||||
|
@ -17261,6 +17559,12 @@
|
|||
fingerprint = "AEF2 3487 66F3 71C6 89A7 3600 95A4 2FE8 3535 25F9";
|
||||
}];
|
||||
};
|
||||
vinetos = {
|
||||
name = "vinetos";
|
||||
email = "vinetosdev@gmail.com";
|
||||
github = "vinetos";
|
||||
githubId = 10145351;
|
||||
};
|
||||
vinnymeller = {
|
||||
email = "vinnymeller@proton.me";
|
||||
github = "vinnymeller";
|
||||
|
@ -17364,6 +17668,12 @@
|
|||
githubId = 3413119;
|
||||
name = "Vonfry";
|
||||
};
|
||||
votava = {
|
||||
email = "votava@gmail.com";
|
||||
github = "janvotava";
|
||||
githubId = 367185;
|
||||
name = "Jan Votava";
|
||||
};
|
||||
vq = {
|
||||
email = "vq@erq.se";
|
||||
github = "vq";
|
||||
|
@ -17947,6 +18257,12 @@
|
|||
githubId = 73759599;
|
||||
name = "Yaya";
|
||||
};
|
||||
yboettcher = {
|
||||
name = "Yannik Böttcher";
|
||||
github = "yboettcher";
|
||||
githubId = 39460066;
|
||||
email = "yannikboettcher@outlook.de";
|
||||
};
|
||||
ydlr = {
|
||||
name = "ydlr";
|
||||
email = "ydlr@ydlr.io";
|
||||
|
@ -18266,12 +18582,6 @@
|
|||
github = "zfnmxt";
|
||||
githubId = 37446532;
|
||||
};
|
||||
zgrannan = {
|
||||
email = "zgrannan@gmail.com";
|
||||
github = "zgrannan";
|
||||
githubId = 1141948;
|
||||
name = "Zack Grannan";
|
||||
};
|
||||
zhaofengli = {
|
||||
email = "hello@zhaofeng.li";
|
||||
matrix = "@zhaofeng:zhaofeng.li";
|
||||
|
@ -18303,6 +18613,19 @@
|
|||
githubId = 1108325;
|
||||
name = "Théo Zimmermann";
|
||||
};
|
||||
zmitchell = {
|
||||
name = "Zach Mitchell";
|
||||
email = "zmitchell@fastmail.com";
|
||||
matrix = "@zmitchell:matrix.org";
|
||||
github = "zmitchell";
|
||||
githubId = 10246891;
|
||||
};
|
||||
znewman01 = {
|
||||
email = "znewman01@gmail.com";
|
||||
github = "znewman01";
|
||||
githubId = 873857;
|
||||
name = "Zack Newman";
|
||||
};
|
||||
zoedsoupe = {
|
||||
github = "zoedsoupe";
|
||||
githubId = 44469426;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#! /usr/bin/env nix-shell
|
||||
#! nix-shell -i bash -p nix curl jq nix-prefetch-github git gnused -I nixpkgs=.
|
||||
#! nix-shell -i bash -p nix curl jq git gnused -I nixpkgs=.
|
||||
|
||||
# See regenerate-hackage-packages.sh for details on the purpose of this script.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#! /usr/bin/env nix-shell
|
||||
#! nix-shell -i bash -p nix curl jq nix-prefetch-github git gnused gnugrep -I nixpkgs=.
|
||||
#! nix-shell -i bash -p nix curl jq git gnused gnugrep -I nixpkgs=.
|
||||
# shellcheck shell=bash
|
||||
|
||||
set -eu -o pipefail
|
||||
|
|
|
@ -86,6 +86,7 @@ luuid,,,,,,
|
|||
luv,,,,1.44.2-1,,
|
||||
lush.nvim,https://github.com/rktjmp/lush.nvim,,,,,teto
|
||||
lyaml,,,,,,lblasc
|
||||
magick,,,,,,donovanglover
|
||||
markdown,,,,,,
|
||||
mediator_lua,,,,,,
|
||||
mpack,,,,,,
|
||||
|
|
|
|
@ -6,6 +6,7 @@ use warnings;
|
|||
|
||||
use CPAN::Meta();
|
||||
use CPANPLUS::Backend();
|
||||
use MIME::Base64;
|
||||
use Module::CoreList;
|
||||
use Getopt::Long::Descriptive qw( describe_options );
|
||||
use JSON::PP qw( encode_json );
|
||||
|
@ -354,6 +355,11 @@ sub render_license {
|
|||
return $license_line;
|
||||
}
|
||||
|
||||
sub sha256_to_sri {
|
||||
my ($sha256) = @_;
|
||||
return "sha256-" . encode_base64(pack("H*", $sha256), '');
|
||||
}
|
||||
|
||||
my ( $opt, $module_name ) = handle_opts();
|
||||
|
||||
Log::Log4perl->easy_init(
|
||||
|
@ -380,8 +386,9 @@ INFO( "package: ", $module->package, " (", "$pkg_name-$pkg_version", ", ", $attr
|
|||
INFO( "path: ", $module->path );
|
||||
|
||||
my $tar_path = $module->fetch();
|
||||
my $sri_hash = sha256_to_sri($module->status->checksum_value);
|
||||
INFO( "downloaded to: ", $tar_path );
|
||||
INFO( "sha-256: ", $module->status->checksum_value );
|
||||
INFO( "hash: ", $sri_hash );
|
||||
|
||||
my $pkg_path = $module->extract();
|
||||
INFO( "unpacked to: ", $pkg_path );
|
||||
|
@ -436,7 +443,7 @@ print <<EOF;
|
|||
version = "$pkg_version";
|
||||
src = fetchurl {
|
||||
url = "mirror://cpan/${\$module->path}/${\$module->package}";
|
||||
sha256 = "${\$module->status->checksum_value}";
|
||||
hash = "$sri_hash";
|
||||
};
|
||||
EOF
|
||||
print <<EOF if scalar @build_deps > 0;
|
||||
|
|
|
@ -570,6 +570,7 @@ with lib.maintainers; {
|
|||
ralith
|
||||
dandellion
|
||||
sumnerevans
|
||||
nickcao
|
||||
];
|
||||
scope = "Maintain the ecosystem around Matrix, a decentralized messenger.";
|
||||
shortName = "Matrix";
|
||||
|
@ -820,9 +821,7 @@ with lib.maintainers; {
|
|||
};
|
||||
|
||||
sphinx = {
|
||||
members = [
|
||||
SuperSandro2000
|
||||
];
|
||||
members = [ ];
|
||||
scope = "Maintain Sphinx related packages.";
|
||||
shortName = "Sphinx";
|
||||
};
|
||||
|
|
4
nixos/doc/manual/common.nix
Normal file
4
nixos/doc/manual/common.nix
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
outputPath = "share/doc/nixos";
|
||||
indexPath = "index.html";
|
||||
}
|
|
@ -11,6 +11,8 @@ $ nix-build nixos/release.nix -A manual.x86_64-linux
|
|||
|
||||
If the build succeeds, the manual will be in `./result/share/doc/nixos/index.html`.
|
||||
|
||||
There's also [a convenient development daemon](https://nixos.org/manual/nixpkgs/unstable/#sec-contributing-devmode).
|
||||
|
||||
**Contributing to the man pages**
|
||||
|
||||
The man pages are written in [DocBook] which is XML.
|
||||
|
|
|
@ -16,6 +16,8 @@ let
|
|||
|
||||
lib = pkgs.lib;
|
||||
|
||||
common = import ./common.nix;
|
||||
|
||||
manpageUrls = pkgs.path + "/doc/manpage-urls.json";
|
||||
|
||||
# We need to strip references to /nix/store/* from options,
|
||||
|
@ -63,6 +65,9 @@ let
|
|||
optionIdPrefix = "test-opt-";
|
||||
};
|
||||
|
||||
testDriverMachineDocstrings = pkgs.callPackage
|
||||
../../../nixos/lib/test-driver/nixos-test-driver-docstrings.nix {};
|
||||
|
||||
prepareManualFromMD = ''
|
||||
cp -r --no-preserve=all $inputs/* .
|
||||
|
||||
|
@ -75,11 +80,13 @@ let
|
|||
substituteInPlace ./nixos-options.md \
|
||||
--replace \
|
||||
'@NIXOS_OPTIONS_JSON@' \
|
||||
${optionsDoc.optionsJSON}/share/doc/nixos/options.json
|
||||
${optionsDoc.optionsJSON}/${common.outputPath}/options.json
|
||||
substituteInPlace ./development/writing-nixos-tests.section.md \
|
||||
--replace \
|
||||
'@NIXOS_TEST_OPTIONS_JSON@' \
|
||||
${testOptionsDoc.optionsJSON}/share/doc/nixos/options.json
|
||||
${testOptionsDoc.optionsJSON}/${common.outputPath}/options.json
|
||||
sed -e '/@PYTHON_MACHINE_METHODS@/ {' -e 'r ${testDriverMachineDocstrings}/machine-methods.md' -e 'd' -e '}' \
|
||||
-i ./development/writing-nixos-tests.section.md
|
||||
'';
|
||||
|
||||
in rec {
|
||||
|
@ -94,7 +101,7 @@ in rec {
|
|||
}
|
||||
''
|
||||
# Generate the HTML manual.
|
||||
dst=$out/share/doc/nixos
|
||||
dst=$out/${common.outputPath}
|
||||
mkdir -p $dst
|
||||
|
||||
cp ${../../../doc/style.css} $dst/style.css
|
||||
|
@ -115,7 +122,7 @@ in rec {
|
|||
--toc-depth 1 \
|
||||
--chunk-toc-depth 1 \
|
||||
./manual.md \
|
||||
$dst/index.html
|
||||
$dst/${common.indexPath}
|
||||
|
||||
mkdir -p $out/nix-support
|
||||
echo "nix-build out $out" >> $out/nix-support/hydra-build-products
|
||||
|
@ -126,7 +133,7 @@ in rec {
|
|||
manual = manualHTML;
|
||||
|
||||
# Index page of the NixOS manual.
|
||||
manualHTMLIndex = "${manualHTML}/share/doc/nixos/index.html";
|
||||
manualHTMLIndex = "${manualHTML}/${common.outputPath}/${common.indexPath}";
|
||||
|
||||
manualEpub = runCommand "nixos-manual-epub"
|
||||
{ nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin buildPackages.zip ];
|
||||
|
@ -157,7 +164,7 @@ in rec {
|
|||
}
|
||||
''
|
||||
# Generate the epub manual.
|
||||
dst=$out/share/doc/nixos
|
||||
dst=$out/${common.outputPath}
|
||||
|
||||
xsltproc \
|
||||
--param chapter.autolabel 0 \
|
||||
|
@ -192,7 +199,7 @@ in rec {
|
|||
mkdir -p $out/share/man/man5
|
||||
nixos-render-docs -j $NIX_BUILD_CORES options manpage \
|
||||
--revision ${lib.escapeShellArg revision} \
|
||||
${optionsJSON}/share/doc/nixos/options.json \
|
||||
${optionsJSON}/${common.outputPath}/options.json \
|
||||
$out/share/man/man5/configuration.nix.5
|
||||
'';
|
||||
|
||||
|
|
|
@ -139,210 +139,7 @@ to Python as `machine_a`.
|
|||
|
||||
The following methods are available on machine objects:
|
||||
|
||||
`start`
|
||||
|
||||
: Start the virtual machine. This method is asynchronous --- it does
|
||||
not wait for the machine to finish booting.
|
||||
|
||||
`shutdown`
|
||||
|
||||
: Shut down the machine, waiting for the VM to exit.
|
||||
|
||||
`crash`
|
||||
|
||||
: Simulate a sudden power failure, by telling the VM to exit
|
||||
immediately.
|
||||
|
||||
`block`
|
||||
|
||||
: Simulate unplugging the Ethernet cable that connects the machine to
|
||||
the other machines.
|
||||
|
||||
`unblock`
|
||||
|
||||
: Undo the effect of `block`.
|
||||
|
||||
`screenshot`
|
||||
|
||||
: Take a picture of the display of the virtual machine, in PNG format.
|
||||
The screenshot is linked from the HTML log.
|
||||
|
||||
`get_screen_text_variants`
|
||||
|
||||
: Return a list of different interpretations of what is currently
|
||||
visible on the machine's screen using optical character
|
||||
recognition. The number and order of the interpretations is not
|
||||
specified and is subject to change, but if no exception is raised at
|
||||
least one will be returned.
|
||||
|
||||
::: {.note}
|
||||
This requires [`enableOCR`](#test-opt-enableOCR) to be set to `true`.
|
||||
:::
|
||||
|
||||
`get_screen_text`
|
||||
|
||||
: Return a textual representation of what is currently visible on the
|
||||
machine's screen using optical character recognition.
|
||||
|
||||
::: {.note}
|
||||
This requires [`enableOCR`](#test-opt-enableOCR) to be set to `true`.
|
||||
:::
|
||||
|
||||
`send_monitor_command`
|
||||
|
||||
: Send a command to the QEMU monitor. This is rarely used, but allows
|
||||
doing stuff such as attaching virtual USB disks to a running
|
||||
machine.
|
||||
|
||||
`send_key`
|
||||
|
||||
: Simulate pressing keys on the virtual keyboard, e.g.,
|
||||
`send_key("ctrl-alt-delete")`.
|
||||
|
||||
`send_chars`
|
||||
|
||||
: Simulate typing a sequence of characters on the virtual keyboard,
|
||||
e.g., `send_chars("foobar\n")` will type the string `foobar`
|
||||
followed by the Enter key.
|
||||
|
||||
`send_console`
|
||||
|
||||
: Send keys to the kernel console. This allows interaction with the systemd
|
||||
emergency mode, for example. Takes a string that is sent, e.g.,
|
||||
`send_console("\n\nsystemctl default\n")`.
|
||||
|
||||
`execute`
|
||||
|
||||
: Execute a shell command, returning a list `(status, stdout)`.
|
||||
|
||||
Commands are run with `set -euo pipefail` set:
|
||||
|
||||
- If several commands are separated by `;` and one fails, the
|
||||
command as a whole will fail.
|
||||
|
||||
- For pipelines, the last non-zero exit status will be returned
|
||||
(if there is one; otherwise zero will be returned).
|
||||
|
||||
- Dereferencing unset variables fails the command.
|
||||
|
||||
- It will wait for stdout to be closed.
|
||||
|
||||
If the command detaches, it must close stdout, as `execute` will wait
|
||||
for this to consume all output reliably. This can be achieved by
|
||||
redirecting stdout to stderr `>&2`, to `/dev/console`, `/dev/null` or
|
||||
a file. Examples of detaching commands are `sleep 365d &`, where the
|
||||
shell forks a new process that can write to stdout and `xclip -i`, where
|
||||
the `xclip` command itself forks without closing stdout.
|
||||
|
||||
Takes an optional parameter `check_return` that defaults to `True`.
|
||||
Setting this parameter to `False` will not check for the return code
|
||||
and return -1 instead. This can be used for commands that shut down
|
||||
the VM and would therefore break the pipe that would be used for
|
||||
retrieving the return code.
|
||||
|
||||
A timeout for the command can be specified (in seconds) using the optional
|
||||
`timeout` parameter, e.g., `execute(cmd, timeout=10)` or
|
||||
`execute(cmd, timeout=None)`. The default is 900 seconds.
|
||||
|
||||
`succeed`
|
||||
|
||||
: Execute a shell command, raising an exception if the exit status is
|
||||
not zero, otherwise returning the standard output. Similar to `execute`,
|
||||
except that the timeout is `None` by default. See `execute` for details on
|
||||
command execution.
|
||||
|
||||
`fail`
|
||||
|
||||
: Like `succeed`, but raising an exception if the command returns a zero
|
||||
status.
|
||||
|
||||
`wait_until_succeeds`
|
||||
|
||||
: Repeat a shell command with 1-second intervals until it succeeds.
|
||||
Has a default timeout of 900 seconds which can be modified, e.g.
|
||||
`wait_until_succeeds(cmd, timeout=10)`. See `execute` for details on
|
||||
command execution.
|
||||
|
||||
`wait_until_fails`
|
||||
|
||||
: Like `wait_until_succeeds`, but repeating the command until it fails.
|
||||
|
||||
`wait_for_unit`
|
||||
|
||||
: Wait until the specified systemd unit has reached the "active"
|
||||
state.
|
||||
|
||||
`wait_for_file`
|
||||
|
||||
: Wait until the specified file exists.
|
||||
|
||||
`wait_for_open_port`
|
||||
|
||||
: Wait until a process is listening on the given TCP port and IP address
|
||||
(default `localhost`).
|
||||
|
||||
`wait_for_closed_port`
|
||||
|
||||
: Wait until nobody is listening on the given TCP port and IP address
|
||||
(default `localhost`).
|
||||
|
||||
`wait_for_x`
|
||||
|
||||
: Wait until the X11 server is accepting connections.
|
||||
|
||||
`wait_for_text`
|
||||
|
||||
: Wait until the supplied regular expressions matches the textual
|
||||
contents of the screen by using optical character recognition (see
|
||||
`get_screen_text` and `get_screen_text_variants`).
|
||||
|
||||
::: {.note}
|
||||
This requires [`enableOCR`](#test-opt-enableOCR) to be set to `true`.
|
||||
:::
|
||||
|
||||
`wait_for_console_text`
|
||||
|
||||
: Wait until the supplied regular expressions match a line of the
|
||||
serial console output. This method is useful when OCR is not
|
||||
possible or accurate enough.
|
||||
|
||||
`wait_for_window`
|
||||
|
||||
: Wait until an X11 window has appeared whose name matches the given
|
||||
regular expression, e.g., `wait_for_window("Terminal")`.
|
||||
|
||||
`copy_from_host`
|
||||
|
||||
: Copies a file from host to machine, e.g.,
|
||||
`copy_from_host("myfile", "/etc/my/important/file")`.
|
||||
|
||||
The first argument is the file on the host. The file needs to be
|
||||
accessible while building the nix derivation. The second argument is
|
||||
the location of the file on the machine.
|
||||
|
||||
`systemctl`
|
||||
|
||||
: Runs `systemctl` commands with optional support for
|
||||
`systemctl --user`
|
||||
|
||||
```py
|
||||
machine.systemctl("list-jobs --no-pager") # runs `systemctl list-jobs --no-pager`
|
||||
machine.systemctl("list-jobs --no-pager", "any-user") # spawns a shell for `any-user` and runs `systemctl --user list-jobs --no-pager`
|
||||
```
|
||||
|
||||
`shell_interact`
|
||||
|
||||
: Allows you to directly interact with the guest shell. This should
|
||||
only be used during test development, not in production tests.
|
||||
Killing the interactive session with `Ctrl-d` or `Ctrl-c` also ends
|
||||
the guest session.
|
||||
|
||||
`console_interact`
|
||||
|
||||
: Allows you to directly interact with QEMU's stdin. This should
|
||||
only be used during test development, not in production tests.
|
||||
Output from QEMU is only read line-wise. `Ctrl-c` kills QEMU and
|
||||
`Ctrl-d` closes console and returns to the test runner.
|
||||
@PYTHON_MACHINE_METHODS@
|
||||
|
||||
To test user units declared by `systemd.user.services` the optional
|
||||
`user` argument can be used:
|
||||
|
|
|
@ -249,14 +249,14 @@ update /etc/fstab.
|
|||
which will be used by the boot partition.
|
||||
|
||||
```ShellSession
|
||||
# parted /dev/sda -- mkpart primary 512MB -8GB
|
||||
# parted /dev/sda -- mkpart root ext4 512MB -8GB
|
||||
```
|
||||
|
||||
3. Next, add a *swap* partition. The size required will vary according
|
||||
to needs, here a 8GB one is created.
|
||||
|
||||
```ShellSession
|
||||
# parted /dev/sda -- mkpart primary linux-swap -8GB 100%
|
||||
# parted /dev/sda -- mkpart swap linux-swap -8GB 100%
|
||||
```
|
||||
|
||||
::: {.note}
|
||||
|
@ -550,8 +550,8 @@ corresponding configuration Nix expression.
|
|||
### Example partition schemes for NixOS on `/dev/sda` (UEFI)
|
||||
```ShellSession
|
||||
# parted /dev/sda -- mklabel gpt
|
||||
# parted /dev/sda -- mkpart primary 512MB -8GB
|
||||
# parted /dev/sda -- mkpart primary linux-swap -8GB 100%
|
||||
# parted /dev/sda -- mkpart root ext4 512MB -8GB
|
||||
# parted /dev/sda -- mkpart swap linux-swap -8GB 100%
|
||||
# parted /dev/sda -- mkpart ESP fat32 1MB 512MB
|
||||
# parted /dev/sda -- set 3 esp on
|
||||
```
|
||||
|
|
|
@ -441,6 +441,8 @@ In addition to numerous new and upgraded packages, this release has the followin
|
|||
|
||||
- `pkgs.haskell-language-server` will now by default be linked dynamically to improve TemplateHaskell compatibility. To mitigate the increased closure size it will now by default only support our current default ghc (at the moment 9.0.2). Add other ghc versions via e.g. `pkgs.haskell-language-server.override { supportedGhcVersions = [ "90" "92" ]; }`.
|
||||
|
||||
- `pkgs.redis` is now built using the system jemalloc. This disables the experimental active defragmentation feature of redis. Users who require this feature can switch back to redis' vendored version of jemalloc by setting `services.redis.package = pkgs.redis.override { useSystemJemalloc = false; };`.
|
||||
|
||||
## Other Notable Changes {#sec-release-21.11-notable-changes}
|
||||
|
||||
|
||||
|
|
|
@ -83,6 +83,8 @@ In addition to numerous new and updated packages, this release has the following
|
|||
|
||||
- [gitea-actions-runner](https://gitea.com/gitea/act_runner), a CI runner for Gitea/Forgejo Actions. Available as [services.gitea-actions-runner](#opt-services.gitea-actions-runner.instances).
|
||||
|
||||
- [evdevremapkeys](https://github.com/philipl/evdevremapkeys), a daemon to remap key events. Available as [services.evdevremapkeys](#opt-services.evdevremapkeys.enable).
|
||||
|
||||
- [gmediarender](https://github.com/hzeller/gmrender-resurrect), a simple, headless UPnP/DLNA renderer. Available as [services.gmediarender](options.html#opt-services.gmediarender.enable).
|
||||
|
||||
- [go2rtc](https://github.com/AlexxIT/go2rtc), a camera streaming appliation with support for RTSP, WebRTC, HomeKit, FFMPEG, RTMP and other protocols. Available as [services.go2rtc](options.html#opt-services.go2rtc.enable).
|
||||
|
|
|
@ -18,12 +18,24 @@
|
|||
|
||||
- [GoToSocial](https://gotosocial.org/), an ActivityPub social network server, written in Golang. Available as [services.gotosocial](#opt-services.gotosocial.enable).
|
||||
|
||||
- [Typesense](https://github.com/typesense/typesense), a fast, typo-tolerant search engine for building delightful search experiences. Available as [services.typesense](#opt-services.typesense.enable).
|
||||
|
||||
- [Anuko Time Tracker](https://github.com/anuko/timetracker), a simple, easy to use, open source time tracking system. Available as [services.anuko-time-tracker](#opt-services.anuko-time-tracker.enable).
|
||||
|
||||
- [sitespeed-io](https://sitespeed.io), a tool that can generate metrics (timings, diagnostics) for websites. Available as [services.sitespeed-io](#opt-services.sitespeed-io.enable).
|
||||
|
||||
- [Apache Guacamole](https://guacamole.apache.org/), a cross-platform, clientless remote desktop gateway. Available as [services.guacamole-server](#opt-services.guacamole-server.enable) and [services.guacamole-client](#opt-services.guacamole-client.enable) services.
|
||||
|
||||
- [pgBouncer](https://www.pgbouncer.org), a PostgreSQL connection pooler. Available as [services.pgbouncer](#opt-services.pgbouncer.enable).
|
||||
|
||||
- [trust-dns](https://trust-dns.org/), a Rust based DNS server built to be safe and secure from the ground up. Available as [services.trust-dns](#opt-services.trust-dns.enable).
|
||||
|
||||
- [osquery](https://www.osquery.io/), a SQL powered operating system instrumentation, monitoring, and analytics.
|
||||
|
||||
- [ebusd](https://ebusd.eu), a daemon for handling communication with eBUS devices connected to a 2-wire bus system (“energy bus” used by numerous heating systems). Available as [services.ebusd](#opt-services.ebusd.enable).
|
||||
|
||||
- [systemd-sysupdate](https://www.freedesktop.org/software/systemd/man/systemd-sysupdate.html), atomically updates the host OS, container images, portable service images or other sources. Available as [systemd.sysupdate](opt-systemd.sysupdate).
|
||||
|
||||
## Backward Incompatibilities {#sec-release-23.11-incompatibilities}
|
||||
|
||||
- The `boot.loader.raspberryPi` options have been marked deprecated, with intent for removal for NixOS 24.11. They had a limited use-case, and do not work like people expect. They required either very old installs ([before mid-2019](https://github.com/NixOS/nixpkgs/pull/62462)) or customized builds out of scope of the standard and generic AArch64 support. That option set never supported the Raspberry Pi 4 family of devices.
|
||||
|
@ -46,6 +58,8 @@
|
|||
|
||||
- `getent` has been moved from `glibc`'s `bin` output to its own dedicated output, reducing closure size for many dependents. Dependents using the `getent` alias should not be affected; others should move from using `glibc.bin` or `getBin glibc` to `getent` (which also improves compatibility with non-glibc platforms).
|
||||
|
||||
- The `services.ananicy.extraRules` option now has the type of `listOf attrs` instead of `string`.
|
||||
|
||||
- `etcd` has been updated to 3.5, you will want to read the [3.3 to 3.4](https://etcd.io/docs/v3.5/upgrades/upgrade_3_4/) and [3.4 to 3.5](https://etcd.io/docs/v3.5/upgrades/upgrade_3_5/) upgrade guides
|
||||
|
||||
- `consul` has been updated to `1.16.0`. See the [release note](https://github.com/hashicorp/consul/releases/tag/v1.16.0) for more details. Once a new Consul version has started and upgraded its data directory, it generally cannot be downgraded to the previous version.
|
||||
|
@ -60,6 +74,8 @@
|
|||
|
||||
- `util-linux` is now supported on Darwin and is no longer an alias to `unixtools`. Use the `unixtools.util-linux` package for access to the Apple variants of the utilities.
|
||||
|
||||
- `services.keyd` changed API. Now you can create multiple configuration files.
|
||||
|
||||
- `services.ddclient` has been removed on the request of the upstream maintainer because it is unmaintained and has bugs. Please switch to a different software like `inadyn` or `knsupdate`.
|
||||
|
||||
- The `vlock` program from the `kbd` package has been moved into its own package output and should now be referenced explicitly as `kbd.vlock` or replaced with an alternative such as the standalone `vlock` package or `physlock`.
|
||||
|
@ -72,12 +88,22 @@
|
|||
|
||||
- `services.lemmy.settings.federation` was removed in 0.17.0 and no longer has any effect. To enable federation, the hostname must be set in the configuration file and then federation must be enabled in the admin web UI. See the [release notes](https://github.com/LemmyNet/lemmy/blob/c32585b03429f0f76d1e4ff738786321a0a9df98/RELEASES.md#upgrade-instructions) for more details.
|
||||
|
||||
- `pict-rs` was upgraded from 0.3 to 0.4 and contains an incompatible database & configuration change. To upgrade on systems with `stateVersion = "23.05";` or older follow the migration steps from https://git.asonix.dog/asonix/pict-rs#user-content-0-3-to-0-4-migration-guide and set `services.pict-rs.package = pkgs.pict-rs;`.
|
||||
|
||||
- The following packages in `haskellPackages` have now a separate bin output: `cabal-fmt`, `calligraphy`, `eventlog2html`, `ghc-debug-brick`, `hindent`, `nixfmt`, `releaser`. This means you need to replace e.g. `"${pkgs.haskellPackages.nixfmt}/bin/nixfmt"` with `"${lib.getBin pkgs.haskellPackages.nixfmt}/bin/nixfmt"` or `"${lib.getExe pkgs.haskellPackages.nixfmt}"`. The binaries also won’t be in scope if you rely on them being installed e.g. via `ghcWithPackages`. `environment.packages` picks the `bin` output automatically, so for normal installation no intervention is required. Also, toplevel attributes like `pkgs.nixfmt` are not impacted negatively by this change.
|
||||
|
||||
- `spamassassin` no longer supports the `Hashcash` module. The module needs to be removed from the `loadplugin` list if it was copied over from the default `initPreConf` option.
|
||||
|
||||
- `services.outline.sequelizeArguments` has been removed, as `outline` no longer executes database migrations via the `sequelize` cli.
|
||||
|
||||
- The Caddy module gained a new option named `services.caddy.enableReload` which is enabled by default. It allows reloading the service instead of restarting it, if only a config file has changed. This option must be disabled if you have turned off the [Caddy admin API](https://caddyserver.com/docs/caddyfile/options#admin). If you keep this option enabled, you should consider setting [`grace_period`](https://caddyserver.com/docs/caddyfile/options#grace-period) to a non-infinite value to prevent Caddy from delaying the reload indefinitely.
|
||||
|
||||
- mdraid support is now optional. This reduces initramfs size and prevents the potentially undesired automatic detection and activation of software RAID pools. It is disabled by default in new configurations (determined by `stateVersion`), but the appropriate settings will be generated by `nixos-generate-config` when installing to a software RAID device, so the standard installation procedure should be unaffected. If you have custom configs relying on mdraid, ensure that you use `stateVersion` correctly or set `boot.swraid.enable` manually.
|
||||
|
||||
- The `go-ethereum` package has been updated to v1.12.0. This drops support for proof-of-work. Its GraphQL API now encodes all numeric values as hex strings and the GraphQL UI is updated to version 2.0. The default database has changed from `leveldb` to `pebble` but `leveldb` can be forced with the --db.engine=leveldb flag. The `checkpoint-admin` command was [removed along with trusted checkpoints](https://github.com/ethereum/go-ethereum/pull/27147).
|
||||
|
||||
- The default `kops` version is now 1.27.0 and support for 1.24 and older has been dropped.
|
||||
|
||||
## Other Notable Changes {#sec-release-23.11-notable-changes}
|
||||
|
||||
- The Cinnamon module now enables XDG desktop integration by default. If you are experiencing collisions related to xdg-desktop-portal-gtk you can safely remove `xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ];` from your NixOS configuration.
|
||||
|
@ -96,6 +122,10 @@
|
|||
|
||||
- DocBook option documentation is no longer supported, all module documentation now uses markdown.
|
||||
|
||||
- `buildGoModule` `go-modules` attrs have been renamed to `goModules`.
|
||||
|
||||
- The `fonts.fonts` and `fonts.enableDefaultFonts` options have been renamed to `fonts.packages` and `fonts.enableDefaultPackages` respectively.
|
||||
|
||||
- `services.fail2ban.jails` can now be configured with attribute sets defining settings and filters instead of lines. The stringed options `daemonConfig` and `extraSettings` have respectively been replaced by `daemonSettings` and `jails.DEFAULT.settings` which use attribute sets.
|
||||
|
||||
- The module [services.ankisyncd](#opt-services.ankisyncd.package) has been switched to [anki-sync-server-rs](https://github.com/ankicommunity/anki-sync-server-rs) from the old python version, which was difficult to update, had not been updated in a while, and did not support recent versions of anki.
|
||||
|
@ -112,6 +142,8 @@ The module update takes care of the new config syntax and the data itself (user
|
|||
|
||||
- `programs.gnupg.agent.pinentryFlavor` is now set in `/etc/gnupg/gpg-agent.conf`, and will no longer take precedence over a `pinentry-program` set in `~/.gnupg/gpg-agent.conf`.
|
||||
|
||||
- `wrapHelm` now exposes `passthru.pluginsDir` which can be passed to `helmfile`. For convenience, a top-level package `helmfile-wrapped` has been added, which inherits `passthru.pluginsDir` from `kubernetes-helm-wrapped`. See [#217768](https://github.com/NixOS/nixpkgs/issues/217768) for details.
|
||||
|
||||
## Nixpkgs internals {#sec-release-23.11-nixpkgs-internals}
|
||||
|
||||
- The `qemu-vm.nix` module by default now identifies block devices via
|
||||
|
|
20
nixos/doc/manual/shell.nix
Normal file
20
nixos/doc/manual/shell.nix
Normal file
|
@ -0,0 +1,20 @@
|
|||
let
|
||||
pkgs = import ../../.. {
|
||||
config = {};
|
||||
overlays = [];
|
||||
};
|
||||
|
||||
common = import ./common.nix;
|
||||
inherit (common) outputPath indexPath;
|
||||
|
||||
web-devmode = import ../../../pkgs/tools/nix/web-devmode.nix {
|
||||
inherit pkgs;
|
||||
buildArgs = "../../release.nix -A manualHTML.${builtins.currentSystem}";
|
||||
open = "/${outputPath}/${indexPath}";
|
||||
};
|
||||
in
|
||||
pkgs.mkShell {
|
||||
packages = [
|
||||
web-devmode
|
||||
];
|
||||
}
|
|
@ -31,7 +31,7 @@ evalConfigArgs@
|
|||
, prefix ? []
|
||||
, lib ? import ../../lib
|
||||
, extraModules ? let e = builtins.getEnv "NIXOS_EXTRA_MODULE_PATH";
|
||||
in if e == "" then [] else [(import e)]
|
||||
in lib.optional (e != "") (import e)
|
||||
}:
|
||||
|
||||
let pkgs_ = pkgs;
|
||||
|
|
|
@ -572,7 +572,7 @@ let format' = format; in let
|
|||
${lib.optionalString installBootLoader ''
|
||||
# In this throwaway resource, we only have /dev/vda, but the actual VM may refer to another disk for bootloader, e.g. /dev/vdb
|
||||
# Use this option to create a symlink from vda to any arbitrary device you want.
|
||||
${optionalString (config.boot.loader.grub.device != "/dev/vda") ''
|
||||
${optionalString (config.boot.loader.grub.enable && config.boot.loader.grub.device != "/dev/vda") ''
|
||||
mkdir -p $(dirname ${config.boot.loader.grub.device})
|
||||
ln -s /dev/vda ${config.boot.loader.grub.device}
|
||||
''}
|
||||
|
|
|
@ -63,7 +63,12 @@ in rec {
|
|||
|
||||
assertMacAddress = name: group: attr:
|
||||
optional (attr ? ${name} && ! isMacAddress attr.${name})
|
||||
"Systemd ${group} field `${name}' must be a valid mac address.";
|
||||
"Systemd ${group} field `${name}' must be a valid MAC address.";
|
||||
|
||||
assertNetdevMacAddress = name: group: attr:
|
||||
optional (attr ? ${name} && (! isMacAddress attr.${name} && attr.${name} != "none"))
|
||||
"Systemd ${group} field `${name}` must be a valid MAC address or the special value `none`.";
|
||||
|
||||
|
||||
isPort = i: i >= 0 && i <= 65535;
|
||||
|
||||
|
@ -438,4 +443,21 @@ in rec {
|
|||
${attrsToSection def.sliceConfig}
|
||||
'';
|
||||
};
|
||||
|
||||
# Create a directory that contains systemd definition files from an attrset
|
||||
# that contains the file names as keys and the content as values. The values
|
||||
# in that attrset are determined by the supplied format.
|
||||
definitions = directoryName: format: definitionAttrs:
|
||||
let
|
||||
listOfDefinitions = lib.mapAttrsToList
|
||||
(name: format.generate "${name}.conf")
|
||||
definitionAttrs;
|
||||
in
|
||||
pkgs.runCommand directoryName { } ''
|
||||
mkdir -p $out
|
||||
${(lib.concatStringsSep "\n"
|
||||
(map (pkg: "cp ${pkg} $out/${pkg.name}") listOfDefinitions)
|
||||
)}
|
||||
'';
|
||||
|
||||
}
|
||||
|
|
66
nixos/lib/test-driver/extract-docstrings.py
Normal file
66
nixos/lib/test-driver/extract-docstrings.py
Normal file
|
@ -0,0 +1,66 @@
|
|||
import ast
|
||||
import sys
|
||||
|
||||
"""
|
||||
This program takes all the Machine class methods and prints its methods in
|
||||
markdown-style. These can then be included in the NixOS test driver
|
||||
markdown style, assuming the docstrings themselves are also in markdown.
|
||||
|
||||
These are included in the test driver documentation in the NixOS manual.
|
||||
See https://nixos.org/manual/nixos/stable/#ssec-machine-objects
|
||||
|
||||
The python input looks like this:
|
||||
|
||||
```py
|
||||
...
|
||||
|
||||
class Machine(...):
|
||||
...
|
||||
|
||||
def some_function(self, param1, param2):
|
||||
""
|
||||
documentation string of some_function.
|
||||
foo bar baz.
|
||||
""
|
||||
...
|
||||
```
|
||||
|
||||
Output will be:
|
||||
|
||||
```markdown
|
||||
...
|
||||
|
||||
some_function(param1, param2)
|
||||
|
||||
: documentation string of some_function.
|
||||
foo bar baz.
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
"""
|
||||
|
||||
assert len(sys.argv) == 2
|
||||
|
||||
with open(sys.argv[1], "r") as f:
|
||||
module = ast.parse(f.read())
|
||||
|
||||
class_definitions = (node for node in module.body if isinstance(node, ast.ClassDef))
|
||||
|
||||
machine_class = next(filter(lambda x: x.name == "Machine", class_definitions))
|
||||
assert machine_class is not None
|
||||
|
||||
function_definitions = [
|
||||
node for node in machine_class.body if isinstance(node, ast.FunctionDef)
|
||||
]
|
||||
function_definitions.sort(key=lambda x: x.name)
|
||||
|
||||
for f in function_definitions:
|
||||
docstr = ast.get_docstring(f)
|
||||
if docstr is not None:
|
||||
args = ", ".join((a.arg for a in f.args.args[1:]))
|
||||
args = f"({args})"
|
||||
|
||||
docstr = "\n".join((f" {l}" for l in docstr.strip().splitlines()))
|
||||
|
||||
print(f"{f.name}{args}\n\n:{docstr[1:]}\n")
|
13
nixos/lib/test-driver/nixos-test-driver-docstrings.nix
Normal file
13
nixos/lib/test-driver/nixos-test-driver-docstrings.nix
Normal file
|
@ -0,0 +1,13 @@
|
|||
{ runCommand
|
||||
, python3
|
||||
}:
|
||||
|
||||
let
|
||||
env = { nativeBuildInputs = [ python3 ]; };
|
||||
in
|
||||
|
||||
runCommand "nixos-test-driver-docstrings" env ''
|
||||
mkdir $out
|
||||
python3 ${./extract-docstrings.py} ${./test_driver/machine.py} \
|
||||
> $out/machine-methods.md
|
||||
''
|
|
@ -416,6 +416,10 @@ class Machine:
|
|||
return answer
|
||||
|
||||
def send_monitor_command(self, command: str) -> str:
|
||||
"""
|
||||
Send a command to the QEMU monitor. This allows attaching
|
||||
virtual USB disks to a running machine, among other things.
|
||||
"""
|
||||
self.run_callbacks()
|
||||
message = f"{command}\n".encode()
|
||||
assert self.monitor is not None
|
||||
|
@ -425,9 +429,10 @@ class Machine:
|
|||
def wait_for_unit(
|
||||
self, unit: str, user: Optional[str] = None, timeout: int = 900
|
||||
) -> None:
|
||||
"""Wait for a systemd unit to get into "active" state.
|
||||
Throws exceptions on "failed" and "inactive" states as well as
|
||||
after timing out.
|
||||
"""
|
||||
Wait for a systemd unit to get into "active" state.
|
||||
Throws exceptions on "failed" and "inactive" states as well as after
|
||||
timing out.
|
||||
"""
|
||||
|
||||
def check_active(_: Any) -> bool:
|
||||
|
@ -476,6 +481,19 @@ class Machine:
|
|||
)
|
||||
|
||||
def systemctl(self, q: str, user: Optional[str] = None) -> Tuple[int, str]:
|
||||
"""
|
||||
Runs `systemctl` commands with optional support for
|
||||
`systemctl --user`
|
||||
|
||||
```py
|
||||
# run `systemctl list-jobs --no-pager`
|
||||
machine.systemctl("list-jobs --no-pager")
|
||||
|
||||
# spawn a shell for `any-user` and run
|
||||
# `systemctl --user list-jobs --no-pager`
|
||||
machine.systemctl("list-jobs --no-pager", "any-user")
|
||||
```
|
||||
"""
|
||||
if user is not None:
|
||||
q = q.replace("'", "\\'")
|
||||
return self.execute(
|
||||
|
@ -520,6 +538,38 @@ class Machine:
|
|||
check_output: bool = True,
|
||||
timeout: Optional[int] = 900,
|
||||
) -> Tuple[int, str]:
|
||||
"""
|
||||
Execute a shell command, returning a list `(status, stdout)`.
|
||||
|
||||
Commands are run with `set -euo pipefail` set:
|
||||
|
||||
- If several commands are separated by `;` and one fails, the
|
||||
command as a whole will fail.
|
||||
|
||||
- For pipelines, the last non-zero exit status will be returned
|
||||
(if there is one; otherwise zero will be returned).
|
||||
|
||||
- Dereferencing unset variables fails the command.
|
||||
|
||||
- It will wait for stdout to be closed.
|
||||
|
||||
If the command detaches, it must close stdout, as `execute` will wait
|
||||
for this to consume all output reliably. This can be achieved by
|
||||
redirecting stdout to stderr `>&2`, to `/dev/console`, `/dev/null` or
|
||||
a file. Examples of detaching commands are `sleep 365d &`, where the
|
||||
shell forks a new process that can write to stdout and `xclip -i`, where
|
||||
the `xclip` command itself forks without closing stdout.
|
||||
|
||||
Takes an optional parameter `check_return` that defaults to `True`.
|
||||
Setting this parameter to `False` will not check for the return code
|
||||
and return -1 instead. This can be used for commands that shut down
|
||||
the VM and would therefore break the pipe that would be used for
|
||||
retrieving the return code.
|
||||
|
||||
A timeout for the command can be specified (in seconds) using the optional
|
||||
`timeout` parameter, e.g., `execute(cmd, timeout=10)` or
|
||||
`execute(cmd, timeout=None)`. The default is 900 seconds.
|
||||
"""
|
||||
self.run_callbacks()
|
||||
self.connect()
|
||||
|
||||
|
@ -533,7 +583,7 @@ class Machine:
|
|||
# While sh is bash on NixOS, this is not the case for every distro.
|
||||
# We explicitly call bash here to allow for the driver to boot other distros as well.
|
||||
out_command = (
|
||||
f"{timeout_str} bash -c {shlex.quote(command)} | (base64 --wrap 0; echo)\n"
|
||||
f"{timeout_str} bash -c {shlex.quote(command)} | (base64 -w 0; echo)\n"
|
||||
)
|
||||
|
||||
assert self.shell
|
||||
|
@ -555,10 +605,11 @@ class Machine:
|
|||
return (rc, output.decode(errors="replace"))
|
||||
|
||||
def shell_interact(self, address: Optional[str] = None) -> None:
|
||||
"""Allows you to interact with the guest shell for debugging purposes.
|
||||
|
||||
@address string passed to socat that will be connected to the guest shell.
|
||||
Check the `Running Tests interactivly` chapter of NixOS manual for an example.
|
||||
"""
|
||||
Allows you to directly interact with the guest shell. This should
|
||||
only be used during test development, not in production tests.
|
||||
Killing the interactive session with `Ctrl-d` or `Ctrl-c` also ends
|
||||
the guest session.
|
||||
"""
|
||||
self.connect()
|
||||
|
||||
|
@ -577,12 +628,14 @@ class Machine:
|
|||
pass
|
||||
|
||||
def console_interact(self) -> None:
|
||||
"""Allows you to interact with QEMU's stdin
|
||||
|
||||
The shell can be exited with Ctrl+D. Note that Ctrl+C is not allowed to be used.
|
||||
QEMU's stdout is read line-wise.
|
||||
|
||||
Should only be used during test development, not in the production test."""
|
||||
"""
|
||||
Allows you to directly interact with QEMU's stdin, by forwarding
|
||||
terminal input to the QEMU process.
|
||||
This is for use with the interactive test driver, not for production
|
||||
tests, which run unattended.
|
||||
Output from QEMU is only read line-wise. `Ctrl-c` kills QEMU and
|
||||
`Ctrl-d` closes console and returns to the test runner.
|
||||
"""
|
||||
self.log("Terminal is ready (there is no prompt):")
|
||||
|
||||
assert self.process
|
||||
|
@ -599,7 +652,12 @@ class Machine:
|
|||
self.send_console(char.decode())
|
||||
|
||||
def succeed(self, *commands: str, timeout: Optional[int] = None) -> str:
|
||||
"""Execute each command and check that it succeeds."""
|
||||
"""
|
||||
Execute a shell command, raising an exception if the exit status is
|
||||
not zero, otherwise returning the standard output. Similar to `execute`,
|
||||
except that the timeout is `None` by default. See `execute` for details on
|
||||
command execution.
|
||||
"""
|
||||
output = ""
|
||||
for command in commands:
|
||||
with self.nested(f"must succeed: {command}"):
|
||||
|
@ -611,7 +669,10 @@ class Machine:
|
|||
return output
|
||||
|
||||
def fail(self, *commands: str, timeout: Optional[int] = None) -> str:
|
||||
"""Execute each command and check that it fails."""
|
||||
"""
|
||||
Like `succeed`, but raising an exception if the command returns a zero
|
||||
status.
|
||||
"""
|
||||
output = ""
|
||||
for command in commands:
|
||||
with self.nested(f"must fail: {command}"):
|
||||
|
@ -622,7 +683,11 @@ class Machine:
|
|||
return output
|
||||
|
||||
def wait_until_succeeds(self, command: str, timeout: int = 900) -> str:
|
||||
"""Wait until a command returns success and return its output.
|
||||
"""
|
||||
Repeat a shell command with 1-second intervals until it succeeds.
|
||||
Has a default timeout of 900 seconds which can be modified, e.g.
|
||||
`wait_until_succeeds(cmd, timeout=10)`. See `execute` for details on
|
||||
command execution.
|
||||
Throws an exception on timeout.
|
||||
"""
|
||||
output = ""
|
||||
|
@ -637,8 +702,8 @@ class Machine:
|
|||
return output
|
||||
|
||||
def wait_until_fails(self, command: str, timeout: int = 900) -> str:
|
||||
"""Wait until a command returns failure.
|
||||
Throws an exception on timeout.
|
||||
"""
|
||||
Like `wait_until_succeeds`, but repeating the command until it fails.
|
||||
"""
|
||||
output = ""
|
||||
|
||||
|
@ -690,12 +755,19 @@ class Machine:
|
|||
retry(tty_matches)
|
||||
|
||||
def send_chars(self, chars: str, delay: Optional[float] = 0.01) -> None:
|
||||
"""
|
||||
Simulate typing a sequence of characters on the virtual keyboard,
|
||||
e.g., `send_chars("foobar\n")` will type the string `foobar`
|
||||
followed by the Enter key.
|
||||
"""
|
||||
with self.nested(f"sending keys {repr(chars)}"):
|
||||
for char in chars:
|
||||
self.send_key(char, delay, log=False)
|
||||
|
||||
def wait_for_file(self, filename: str) -> None:
|
||||
"""Waits until the file exists in machine's file system."""
|
||||
"""
|
||||
Waits until the file exists in the machine's file system.
|
||||
"""
|
||||
|
||||
def check_file(_: Any) -> bool:
|
||||
status, _ = self.execute(f"test -e {filename}")
|
||||
|
@ -705,6 +777,11 @@ class Machine:
|
|||
retry(check_file)
|
||||
|
||||
def wait_for_open_port(self, port: int, addr: str = "localhost") -> None:
|
||||
"""
|
||||
Wait until a process is listening on the given TCP port and IP address
|
||||
(default `localhost`).
|
||||
"""
|
||||
|
||||
def port_is_open(_: Any) -> bool:
|
||||
status, _ = self.execute(f"nc -z {addr} {port}")
|
||||
return status == 0
|
||||
|
@ -713,6 +790,11 @@ class Machine:
|
|||
retry(port_is_open)
|
||||
|
||||
def wait_for_closed_port(self, port: int, addr: str = "localhost") -> None:
|
||||
"""
|
||||
Wait until nobody is listening on the given TCP port and IP address
|
||||
(default `localhost`).
|
||||
"""
|
||||
|
||||
def port_is_closed(_: Any) -> bool:
|
||||
status, _ = self.execute(f"nc -z {addr} {port}")
|
||||
return status != 0
|
||||
|
@ -766,6 +848,10 @@ class Machine:
|
|||
self.connected = True
|
||||
|
||||
def screenshot(self, filename: str) -> None:
|
||||
"""
|
||||
Take a picture of the display of the virtual machine, in PNG format.
|
||||
The screenshot will be available in the derivation output.
|
||||
"""
|
||||
if "." not in filename:
|
||||
filename += ".png"
|
||||
if "/" not in filename:
|
||||
|
@ -795,8 +881,21 @@ class Machine:
|
|||
)
|
||||
|
||||
def copy_from_host(self, source: str, target: str) -> None:
|
||||
"""Copy a file from the host into the guest via the `shared_dir` shared
|
||||
among all the VMs (using a temporary directory).
|
||||
"""
|
||||
Copies a file from host to machine, e.g.,
|
||||
`copy_from_host("myfile", "/etc/my/important/file")`.
|
||||
|
||||
The first argument is the file on the host. Note that the "host" refers
|
||||
to the environment in which the test driver runs, which is typically the
|
||||
Nix build sandbox.
|
||||
|
||||
The second argument is the location of the file on the machine that will
|
||||
be written to.
|
||||
|
||||
The file is copied via the `shared_dir` directory which is shared among
|
||||
all the VMs (using a temporary directory).
|
||||
The access rights bits will mimic the ones from the host file and
|
||||
user:group will be root:root.
|
||||
"""
|
||||
host_src = Path(source)
|
||||
vm_target = Path(target)
|
||||
|
@ -848,12 +947,41 @@ class Machine:
|
|||
return _perform_ocr_on_screenshot(screenshot_path, model_ids)
|
||||
|
||||
def get_screen_text_variants(self) -> List[str]:
|
||||
"""
|
||||
Return a list of different interpretations of what is currently
|
||||
visible on the machine's screen using optical character
|
||||
recognition. The number and order of the interpretations is not
|
||||
specified and is subject to change, but if no exception is raised at
|
||||
least one will be returned.
|
||||
|
||||
::: {.note}
|
||||
This requires [`enableOCR`](#test-opt-enableOCR) to be set to `true`.
|
||||
:::
|
||||
"""
|
||||
return self._get_screen_text_variants([0, 1, 2])
|
||||
|
||||
def get_screen_text(self) -> str:
|
||||
"""
|
||||
Return a textual representation of what is currently visible on the
|
||||
machine's screen using optical character recognition.
|
||||
|
||||
::: {.note}
|
||||
This requires [`enableOCR`](#test-opt-enableOCR) to be set to `true`.
|
||||
:::
|
||||
"""
|
||||
return self._get_screen_text_variants([2])[0]
|
||||
|
||||
def wait_for_text(self, regex: str) -> None:
|
||||
"""
|
||||
Wait until the supplied regular expressions matches the textual
|
||||
contents of the screen by using optical character recognition (see
|
||||
`get_screen_text` and `get_screen_text_variants`).
|
||||
|
||||
::: {.note}
|
||||
This requires [`enableOCR`](#test-opt-enableOCR) to be set to `true`.
|
||||
:::
|
||||
"""
|
||||
|
||||
def screen_matches(last: bool) -> bool:
|
||||
variants = self.get_screen_text_variants()
|
||||
for text in variants:
|
||||
|
@ -870,12 +998,9 @@ class Machine:
|
|||
|
||||
def wait_for_console_text(self, regex: str, timeout: int | None = None) -> None:
|
||||
"""
|
||||
Wait for the provided regex to appear on console.
|
||||
For each reads,
|
||||
|
||||
If timeout is None, timeout is infinite.
|
||||
|
||||
`timeout` is in seconds.
|
||||
Wait until the supplied regular expressions match a line of the
|
||||
serial console output.
|
||||
This method is useful when OCR is not possible or inaccurate.
|
||||
"""
|
||||
# Buffer the console output, this is needed
|
||||
# to match multiline regexes.
|
||||
|
@ -903,6 +1028,13 @@ class Machine:
|
|||
def send_key(
|
||||
self, key: str, delay: Optional[float] = 0.01, log: Optional[bool] = True
|
||||
) -> None:
|
||||
"""
|
||||
Simulate pressing keys on the virtual keyboard, e.g.,
|
||||
`send_key("ctrl-alt-delete")`.
|
||||
|
||||
Please also refer to the QEMU documentation for more information on the
|
||||
input syntax: https://en.wikibooks.org/wiki/QEMU/Monitor#sendkey_keys
|
||||
"""
|
||||
key = CHAR_TO_KEY.get(key, key)
|
||||
context = self.nested(f"sending key {repr(key)}") if log else nullcontext()
|
||||
with context:
|
||||
|
@ -911,12 +1043,21 @@ class Machine:
|
|||
time.sleep(delay)
|
||||
|
||||
def send_console(self, chars: str) -> None:
|
||||
r"""
|
||||
Send keys to the kernel console. This allows interaction with the systemd
|
||||
emergency mode, for example. Takes a string that is sent, e.g.,
|
||||
`send_console("\n\nsystemctl default\n")`.
|
||||
"""
|
||||
assert self.process
|
||||
assert self.process.stdin
|
||||
self.process.stdin.write(chars.encode())
|
||||
self.process.stdin.flush()
|
||||
|
||||
def start(self, allow_reboot: bool = False) -> None:
|
||||
"""
|
||||
Start the virtual machine. This method is asynchronous --- it does
|
||||
not wait for the machine to finish booting.
|
||||
"""
|
||||
if self.booted:
|
||||
return
|
||||
|
||||
|
@ -974,6 +1115,9 @@ class Machine:
|
|||
rootlog.log("if you want to keep the VM state, pass --keep-vm-state")
|
||||
|
||||
def shutdown(self) -> None:
|
||||
"""
|
||||
Shut down the machine, waiting for the VM to exit.
|
||||
"""
|
||||
if not self.booted:
|
||||
return
|
||||
|
||||
|
@ -982,6 +1126,9 @@ class Machine:
|
|||
self.wait_for_shutdown()
|
||||
|
||||
def crash(self) -> None:
|
||||
"""
|
||||
Simulate a sudden power failure, by telling the VM to exit immediately.
|
||||
"""
|
||||
if not self.booted:
|
||||
return
|
||||
|
||||
|
@ -999,8 +1146,8 @@ class Machine:
|
|||
self.connected = False
|
||||
|
||||
def wait_for_x(self) -> None:
|
||||
"""Wait until it is possible to connect to the X server. Note that
|
||||
testing the existence of /tmp/.X11-unix/X0 is insufficient.
|
||||
"""
|
||||
Wait until it is possible to connect to the X server.
|
||||
"""
|
||||
|
||||
def check_x(_: Any) -> bool:
|
||||
|
@ -1023,6 +1170,10 @@ class Machine:
|
|||
).splitlines()
|
||||
|
||||
def wait_for_window(self, regexp: str) -> None:
|
||||
"""
|
||||
Wait until an X11 window has appeared whose name matches the given
|
||||
regular expression, e.g., `wait_for_window("Terminal")`.
|
||||
"""
|
||||
pattern = re.compile(regexp)
|
||||
|
||||
def window_is_visible(last_try: bool) -> bool:
|
||||
|
@ -1043,20 +1194,26 @@ class Machine:
|
|||
self.succeed(f"sleep {secs}")
|
||||
|
||||
def forward_port(self, host_port: int = 8080, guest_port: int = 80) -> None:
|
||||
"""Forward a TCP port on the host to a TCP port on the guest.
|
||||
"""
|
||||
Forward a TCP port on the host to a TCP port on the guest.
|
||||
Useful during interactive testing.
|
||||
"""
|
||||
self.send_monitor_command(f"hostfwd_add tcp::{host_port}-:{guest_port}")
|
||||
|
||||
def block(self) -> None:
|
||||
"""Make the machine unreachable by shutting down eth1 (the multicast
|
||||
interface used to talk to the other VMs). We keep eth0 up so that
|
||||
the test driver can continue to talk to the machine.
|
||||
"""
|
||||
Simulate unplugging the Ethernet cable that connects the machine to
|
||||
the other machines.
|
||||
This happens by shutting down eth1 (the multicast interface used to talk
|
||||
to the other VMs). eth0 is kept online to still enable the test driver
|
||||
to communicate with the machine.
|
||||
"""
|
||||
self.send_monitor_command("set_link virtio-net-pci.1 off")
|
||||
|
||||
def unblock(self) -> None:
|
||||
"""Make the machine reachable."""
|
||||
"""
|
||||
Undo the effect of `block`.
|
||||
"""
|
||||
self.send_monitor_command("set_link virtio-net-pci.1 on")
|
||||
|
||||
def release(self) -> None:
|
||||
|
|
|
@ -65,7 +65,8 @@ let
|
|||
echo "${builtins.toString vlanNames}" >> testScriptWithTypes
|
||||
echo -n "$testScript" >> testScriptWithTypes
|
||||
|
||||
cat -n testScriptWithTypes
|
||||
echo "Running type check (enable/disable: config.skipTypeCheck)"
|
||||
echo "See https://nixos.org/manual/nixos/stable/#test-opt-skipTypeCheck"
|
||||
|
||||
mypy --no-implicit-optional \
|
||||
--pretty \
|
||||
|
@ -79,6 +80,9 @@ let
|
|||
|
||||
${testDriver}/bin/generate-driver-symbols
|
||||
${lib.optionalString (!config.skipLint) ''
|
||||
echo "Linting test script (enable/disable: config.skipLint)"
|
||||
echo "See https://nixos.org/manual/nixos/stable/#test-opt-skipLint"
|
||||
|
||||
PYFLAKES_BUILTINS="$(
|
||||
echo -n ${lib.escapeShellArg (lib.concatStringsSep "," pythonizedNames)},
|
||||
< ${lib.escapeShellArg "driver-symbols"}
|
||||
|
|
|
@ -42,7 +42,7 @@ let
|
|||
# looking things up.
|
||||
makeCacheConf = { }:
|
||||
let
|
||||
makeCache = fontconfig: pkgs.makeFontsCache { inherit fontconfig; fontDirectories = config.fonts.fonts; };
|
||||
makeCache = fontconfig: pkgs.makeFontsCache { inherit fontconfig; fontDirectories = config.fonts.packages; };
|
||||
cache = makeCache pkgs.fontconfig;
|
||||
cache32 = makeCache pkgs.pkgsi686Linux.fontconfig;
|
||||
in
|
||||
|
@ -51,7 +51,7 @@ let
|
|||
<!DOCTYPE fontconfig SYSTEM 'urn:fontconfig:fonts.dtd'>
|
||||
<fontconfig>
|
||||
<!-- Font directories -->
|
||||
${concatStringsSep "\n" (map (font: "<dir>${font}</dir>") config.fonts.fonts)}
|
||||
${concatStringsSep "\n" (map (font: "<dir>${font}</dir>") config.fonts.packages)}
|
||||
${optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) ''
|
||||
<!-- Pre-generated font caches -->
|
||||
<cachedir>${cache}</cachedir>
|
||||
|
|
|
@ -9,7 +9,7 @@ let
|
|||
x11Fonts = pkgs.runCommand "X11-fonts" { preferLocalBuild = true; } ''
|
||||
mkdir -p "$out/share/X11/fonts"
|
||||
font_regexp='.*\.\(ttf\|ttc\|otb\|otf\|pcf\|pfa\|pfb\|bdf\)\(\.gz\)?'
|
||||
find ${toString config.fonts.fonts} -regex "$font_regexp" \
|
||||
find ${toString config.fonts.packages} -regex "$font_regexp" \
|
||||
-exec ln -sf -t "$out/share/X11/fonts" '{}' \;
|
||||
cd "$out/share/X11/fonts"
|
||||
${optionalString cfg.decompressFonts ''
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.fonts;
|
||||
|
||||
defaultFonts =
|
||||
[ pkgs.dejavu_fonts
|
||||
pkgs.freefont_ttf
|
||||
pkgs.gyre-fonts # TrueType substitutes for standard PostScript fonts
|
||||
pkgs.liberation_ttf
|
||||
pkgs.unifont
|
||||
pkgs.noto-fonts-emoji
|
||||
];
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(mkRemovedOptionModule [ "fonts" "enableCoreFonts" ] "Use fonts.fonts = [ pkgs.corefonts ]; instead.")
|
||||
];
|
||||
|
||||
options = {
|
||||
|
||||
fonts = {
|
||||
|
||||
# TODO: find another name for it.
|
||||
fonts = mkOption {
|
||||
type = types.listOf types.path;
|
||||
default = [];
|
||||
example = literalExpression "[ pkgs.dejavu_fonts ]";
|
||||
description = lib.mdDoc "List of primary font paths.";
|
||||
};
|
||||
|
||||
enableDefaultFonts = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc ''
|
||||
Enable a basic set of fonts providing several font styles
|
||||
and families and reasonable coverage of Unicode.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = { fonts.fonts = mkIf cfg.enableDefaultFonts defaultFonts; };
|
||||
}
|
|
@ -3,31 +3,21 @@
|
|||
with lib;
|
||||
|
||||
{
|
||||
|
||||
options = {
|
||||
|
||||
fonts = {
|
||||
|
||||
enableGhostscriptFonts = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc ''
|
||||
Whether to add the fonts provided by Ghostscript (such as
|
||||
various URW fonts and the “Base-14” Postscript fonts) to the
|
||||
list of system fonts, making them available to X11
|
||||
applications.
|
||||
'';
|
||||
};
|
||||
|
||||
fonts.enableGhostscriptFonts = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc ''
|
||||
Whether to add the fonts provided by Ghostscript (such as
|
||||
various URW fonts and the “Base-14” Postscript fonts) to the
|
||||
list of system fonts, making them available to X11
|
||||
applications.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
config = mkIf config.fonts.enableGhostscriptFonts {
|
||||
|
||||
fonts.fonts = [ "${pkgs.ghostscript}/share/ghostscript/fonts" ];
|
||||
|
||||
fonts.packages = [ "${pkgs.ghostscript}/share/ghostscript/fonts" ];
|
||||
};
|
||||
|
||||
}
|
||||
|
|
43
nixos/modules/config/fonts/packages.nix
Normal file
43
nixos/modules/config/fonts/packages.nix
Normal file
|
@ -0,0 +1,43 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.fonts;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRemovedOptionModule [ "fonts" "enableCoreFonts" ] "Use fonts.packages = [ pkgs.corefonts ]; instead.")
|
||||
(lib.mkRenamedOptionModule [ "fonts" "enableDefaultFonts" ] [ "fonts" "enableDefaultPackages" ])
|
||||
(lib.mkRenamedOptionModule [ "fonts" "fonts" ] [ "fonts" "packages" ])
|
||||
];
|
||||
|
||||
options = {
|
||||
fonts = {
|
||||
packages = lib.mkOption {
|
||||
type = with lib.types; listOf path;
|
||||
default = [];
|
||||
example = lib.literalExpression "[ pkgs.dejavu_fonts ]";
|
||||
description = lib.mdDoc "List of primary font packages.";
|
||||
};
|
||||
|
||||
enableDefaultPackages = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc ''
|
||||
Enable a basic set of fonts providing several styles
|
||||
and families and reasonable coverage of Unicode.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
fonts.packages = lib.mkIf cfg.enableDefaultPackages (with pkgs; [
|
||||
dejavu_fonts
|
||||
freefont_ttf
|
||||
gyre-fonts # TrueType substitutes for standard PostScript fonts
|
||||
liberation_ttf
|
||||
unifont
|
||||
noto-fonts-emoji
|
||||
]);
|
||||
};
|
||||
}
|
|
@ -3,12 +3,13 @@
|
|||
configuration to work.
|
||||
|
||||
See also
|
||||
- ./nix.nix
|
||||
- ./nix-flakes.nix
|
||||
- ./nix.nix
|
||||
- ./nix-flakes.nix
|
||||
*/
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
mkDefault
|
||||
mkIf
|
||||
mkOption
|
||||
stringAfter
|
||||
|
@ -21,13 +22,42 @@ in
|
|||
{
|
||||
options = {
|
||||
nix = {
|
||||
channel = {
|
||||
enable = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
Whether the `nix-channel` command and state files are made available on the machine.
|
||||
|
||||
The following files are initialized when enabled:
|
||||
- `/nix/var/nix/profiles/per-user/root/channels`
|
||||
- `/root/.nix-channels`
|
||||
- `$HOME/.nix-defexpr/channels` (on login)
|
||||
|
||||
Disabling this option will not remove the state files from the system.
|
||||
'';
|
||||
type = types.bool;
|
||||
default = true;
|
||||
};
|
||||
};
|
||||
|
||||
nixPath = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [
|
||||
"nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
|
||||
"nixos-config=/etc/nixos/configuration.nix"
|
||||
"/nix/var/nix/profiles/per-user/root/channels"
|
||||
];
|
||||
default =
|
||||
if cfg.channel.enable
|
||||
then [
|
||||
"nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
|
||||
"nixos-config=/etc/nixos/configuration.nix"
|
||||
"/nix/var/nix/profiles/per-user/root/channels"
|
||||
]
|
||||
else [ ];
|
||||
defaultText = ''
|
||||
if nix.channel.enable
|
||||
then [
|
||||
"nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
|
||||
"nixos-config=/etc/nixos/configuration.nix"
|
||||
"/nix/var/nix/profiles/per-user/root/channels"
|
||||
]
|
||||
else [];
|
||||
'';
|
||||
description = lib.mdDoc ''
|
||||
The default Nix expression search path, used by the Nix
|
||||
evaluator to look up paths enclosed in angle brackets
|
||||
|
@ -49,22 +79,30 @@ in
|
|||
config = mkIf cfg.enable {
|
||||
|
||||
environment.extraInit =
|
||||
''
|
||||
mkIf cfg.channel.enable ''
|
||||
if [ -e "$HOME/.nix-defexpr/channels" ]; then
|
||||
export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}"
|
||||
fi
|
||||
'';
|
||||
|
||||
environment.extraSetup = mkIf (!cfg.channel.enable) ''
|
||||
rm --force $out/bin/nix-channel
|
||||
'';
|
||||
|
||||
# NIX_PATH has a non-empty default according to Nix docs, so we don't unset
|
||||
# it when empty.
|
||||
environment.sessionVariables = {
|
||||
NIX_PATH = cfg.nixPath;
|
||||
};
|
||||
|
||||
system.activationScripts.nix-channel = stringAfter [ "etc" "users" ]
|
||||
''
|
||||
nix.settings.nix-path = mkIf (! cfg.channel.enable) (mkDefault "");
|
||||
|
||||
system.activationScripts.nix-channel = mkIf cfg.channel.enable
|
||||
(stringAfter [ "etc" "users" ] ''
|
||||
# Subscribe the root user to the NixOS channel by default.
|
||||
if [ ! -e "/root/.nix-channels" ]; then
|
||||
echo "${config.system.defaultChannel} nixos" > "/root/.nix-channels"
|
||||
fi
|
||||
'';
|
||||
'');
|
||||
};
|
||||
}
|
||||
|
|
|
@ -26,7 +26,12 @@ with lib;
|
|||
|
||||
fonts.fontconfig.enable = false;
|
||||
|
||||
nixpkgs.overlays = singleton (const (super: {
|
||||
nixpkgs.overlays = singleton (self: super: let
|
||||
packageOverrides = const (python-prev: {
|
||||
# tk feature requires wayland which fails to compile
|
||||
matplotlib = python-prev.matplotlib.override { enableGtk3 = false; enableTk = false; enableQt = false; };
|
||||
});
|
||||
in {
|
||||
beam = super.beam_nox;
|
||||
cairo = super.cairo.override { x11Support = false; };
|
||||
dbus = super.dbus.override { x11Support = false; };
|
||||
|
@ -62,6 +67,8 @@ with lib;
|
|||
pango = super.pango.override { x11Support = false; };
|
||||
pinentry = super.pinentry.override { enabledFlavors = [ "curses" "tty" "emacs" ]; withLibsecret = false; };
|
||||
pipewire = super.pipewire.override { x11Support = false; };
|
||||
python3 = super.python3.override { inherit packageOverrides; };
|
||||
python3Packages = self.python3.pkgs; # required otherwise overlays from above are not forwarded
|
||||
qemu = super.qemu.override { gtkSupport = false; spiceSupport = false; sdlSupport = false; };
|
||||
qrencode = super.qrencode.overrideAttrs (_: { doCheck = false; });
|
||||
qt5 = super.qt5.overrideScope (const (super': {
|
||||
|
@ -72,6 +79,6 @@ with lib;
|
|||
util-linux = super.util-linux.override { translateManpages = false; };
|
||||
vim-full = super.vim-full.override { guiSupport = false; };
|
||||
zbar = super.zbar.override { enableVideo = false; withXorg = false; };
|
||||
}));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ let
|
|||
pkgs.qgnomeplatform-qt6
|
||||
pkgs.adwaita-qt6
|
||||
]
|
||||
else if isQtStyle then [ pkgs.libsForQt5.qtstyleplugins ]
|
||||
else if isQtStyle then [ pkgs.libsForQt5.qtstyleplugins pkgs.qt6Packages.qt6gtk2 ]
|
||||
else if isQt5ct then [ pkgs.libsForQt5.qt5ct pkgs.qt6Packages.qt6ct ]
|
||||
else if isLxqt then [ pkgs.lxqt.lxqt-qtplugin pkgs.lxqt.lxqt-config ]
|
||||
else if isKde then [ pkgs.libsForQt5.plasma-integration pkgs.libsForQt5.systemsettings ]
|
||||
|
@ -86,6 +86,7 @@ in
|
|||
"adwaita-qt"
|
||||
"adwaita-qt6"
|
||||
["libsForQt5" "qtstyleplugins"]
|
||||
["qt6Packages" "qt6gtk2"]
|
||||
];
|
||||
description = lib.mdDoc ''
|
||||
Selects the style to use for Qt applications.
|
||||
|
|
|
@ -252,6 +252,11 @@ in
|
|||
let realDevice' = escapeSystemdPath sw.realDevice;
|
||||
in nameValuePair "mkswap-${sw.deviceName}"
|
||||
{ description = "Initialisation of swap device ${sw.device}";
|
||||
# The mkswap service fails for file-backed swap devices if the
|
||||
# loop module has not been loaded before the service runs.
|
||||
# We add an ordering constraint to run after systemd-modules-load to
|
||||
# avoid this race condition.
|
||||
after = [ "systemd-modules-load.service" ];
|
||||
wantedBy = [ "${realDevice'}.swap" ];
|
||||
before = [ "${realDevice'}.swap" ];
|
||||
path = [ pkgs.util-linux pkgs.e2fsprogs ]
|
||||
|
|
|
@ -147,7 +147,7 @@ foreach my $g (@{$spec->{groups}}) {
|
|||
if (defined $existing) {
|
||||
$g->{gid} = $existing->{gid} if !defined $g->{gid};
|
||||
if ($g->{gid} != $existing->{gid}) {
|
||||
dry_print("warning: not applying", "warning: would not apply", "GID change of group ‘$name’ ($existing->{gid} -> $g->{gid})");
|
||||
dry_print("warning: not applying", "warning: would not apply", "GID change of group ‘$name’ ($existing->{gid} -> $g->{gid}) in /etc/group");
|
||||
$g->{gid} = $existing->{gid};
|
||||
}
|
||||
$g->{password} = $existing->{password}; # do we want this?
|
||||
|
@ -209,7 +209,7 @@ foreach my $u (@{$spec->{users}}) {
|
|||
if (defined $existing) {
|
||||
$u->{uid} = $existing->{uid} if !defined $u->{uid};
|
||||
if ($u->{uid} != $existing->{uid}) {
|
||||
dry_print("warning: not applying", "warning: would not apply", "UID change of user ‘$name’ ($existing->{uid} -> $u->{uid})");
|
||||
dry_print("warning: not applying", "warning: would not apply", "UID change of user ‘$name’ ($existing->{uid} -> $u->{uid}) in /etc/passwd");
|
||||
$u->{uid} = $existing->{uid};
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -7,12 +7,15 @@ with lib;
|
|||
|
||||
options = {
|
||||
|
||||
hardware.usbWwan = {
|
||||
hardware.usb-modeswitch = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc ''
|
||||
Enable this option to support USB WWAN adapters.
|
||||
Enable this option to support certain USB WLAN and WWAN adapters.
|
||||
|
||||
These network adapters initial present themselves as Flash Drives containing their drivers.
|
||||
This option enables automatic switching to the networking mode.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
@ -20,7 +23,11 @@ with lib;
|
|||
|
||||
###### implementation
|
||||
|
||||
config = mkIf config.hardware.usbWwan.enable {
|
||||
imports = [
|
||||
(mkRenamedOptionModule ["hardware" "usbWwan" ] ["hardware" "usb-modeswitch" ])
|
||||
];
|
||||
|
||||
config = mkIf config.hardware.usb-modeswitch.enable {
|
||||
# Attaches device specific handlers.
|
||||
services.udev.packages = with pkgs; [ usb-modeswitch-data ];
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
with lib;
|
||||
{
|
||||
options.hardware.wooting.enable =
|
||||
mkEnableOption (lib.mdDoc "support for Wooting keyboards");
|
||||
options.hardware.wooting.enable = mkEnableOption (lib.mdDoc ''support for Wooting keyboards.
|
||||
Note that users must be in the "input" group for udev rules to apply'');
|
||||
|
||||
config = mkIf config.hardware.wooting.enable {
|
||||
environment.systemPackages = [ pkgs.wootility ];
|
||||
|
|
113
nixos/modules/image/amend-repart-definitions.py
Normal file
113
nixos/modules/image/amend-repart-definitions.py
Normal file
|
@ -0,0 +1,113 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""Amend systemd-repart definiton files.
|
||||
|
||||
In order to avoid Import-From-Derivation (IFD) when building images with
|
||||
systemd-repart, the definition files created by Nix need to be amended with the
|
||||
store paths from the closure.
|
||||
|
||||
This is achieved by adding CopyFiles= instructions to the definition files.
|
||||
|
||||
The arbitrary files configured via `contents` are also added to the definition
|
||||
files using the same mechanism.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import shutil
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def add_contents_to_definition(
|
||||
definition: Path, contents: dict[str, dict[str, str]] | None
|
||||
) -> None:
|
||||
"""Add CopyFiles= instructions to a definition for all files in contents."""
|
||||
if not contents:
|
||||
return
|
||||
|
||||
copy_files_lines: list[str] = []
|
||||
for target, options in contents.items():
|
||||
source = options["source"]
|
||||
|
||||
copy_files_lines.append(f"CopyFiles={source}:{target}\n")
|
||||
|
||||
with open(definition, "a") as f:
|
||||
f.writelines(copy_files_lines)
|
||||
|
||||
|
||||
def add_closure_to_definition(
|
||||
definition: Path, closure: Path | None, strip_nix_store_prefix: bool | None
|
||||
) -> None:
|
||||
"""Add CopyFiles= instructions to a definition for all paths in the closure.
|
||||
|
||||
If strip_nix_store_prefix is True, `/nix/store` is stripped from the target path.
|
||||
"""
|
||||
if not closure:
|
||||
return
|
||||
|
||||
copy_files_lines: list[str] = []
|
||||
with open(closure, "r") as f:
|
||||
for line in f:
|
||||
if not isinstance(line, str):
|
||||
continue
|
||||
|
||||
source = Path(line.strip())
|
||||
target = str(source.relative_to("/nix/store/"))
|
||||
target = f":{target}" if strip_nix_store_prefix else ""
|
||||
|
||||
copy_files_lines.append(f"CopyFiles={source}{target}\n")
|
||||
|
||||
with open(definition, "a") as f:
|
||||
f.writelines(copy_files_lines)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Amend the provided repart definitions by adding CopyFiles= instructions.
|
||||
|
||||
For each file specified in the `contents` field of a partition in the
|
||||
partiton config file, a `CopyFiles=` instruction is added to the
|
||||
corresponding definition file.
|
||||
|
||||
The same is done for every store path of the `closure` field.
|
||||
|
||||
Print the path to a directory that contains the amended repart
|
||||
definitions to stdout.
|
||||
"""
|
||||
partition_config_file = sys.argv[1]
|
||||
if not partition_config_file:
|
||||
print("No partition config file was supplied.")
|
||||
sys.exit(1)
|
||||
|
||||
repart_definitions = sys.argv[2]
|
||||
if not repart_definitions:
|
||||
print("No repart definitions were supplied.")
|
||||
sys.exit(1)
|
||||
|
||||
with open(partition_config_file, "rb") as f:
|
||||
partition_config = json.load(f)
|
||||
|
||||
if not partition_config:
|
||||
print("Partition config is empty.")
|
||||
sys.exit(1)
|
||||
|
||||
temp = tempfile.mkdtemp()
|
||||
shutil.copytree(repart_definitions, temp, dirs_exist_ok=True)
|
||||
|
||||
for name, config in partition_config.items():
|
||||
definition = Path(f"{temp}/{name}.conf")
|
||||
os.chmod(definition, 0o644)
|
||||
|
||||
contents = config.get("contents")
|
||||
add_contents_to_definition(definition, contents)
|
||||
|
||||
closure = config.get("closure")
|
||||
strip_nix_store_prefix = config.get("stripStorePaths")
|
||||
add_closure_to_definition(definition, closure, strip_nix_store_prefix)
|
||||
|
||||
print(temp)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
137
nixos/modules/image/repart.md
Normal file
137
nixos/modules/image/repart.md
Normal file
|
@ -0,0 +1,137 @@
|
|||
# Building Images via `systemd-repart` {#sec-image-repart}
|
||||
|
||||
You can build disk images in NixOS with the `image.repart` option provided by
|
||||
the module [image/repart.nix][]. This module uses `systemd-repart` to build the
|
||||
images and exposes it's entire interface via the `repartConfig` option.
|
||||
|
||||
[image/repart.nix]: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/image/repart.nix
|
||||
|
||||
An example of how to build an image:
|
||||
|
||||
```nix
|
||||
{ config, modulesPath, ... }: {
|
||||
|
||||
imports = [ "${modulesPath}/image/repart.nix" ];
|
||||
|
||||
image.repart = {
|
||||
name = "image";
|
||||
partitions = {
|
||||
"esp" = {
|
||||
contents = {
|
||||
...
|
||||
};
|
||||
repartConfig = {
|
||||
Type = "esp";
|
||||
...
|
||||
};
|
||||
};
|
||||
"root" = {
|
||||
storePaths = [ config.system.build.toplevel ];
|
||||
repartConfig = {
|
||||
Type = "root";
|
||||
Label = "nixos";
|
||||
...
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Nix Store Partition {#sec-image-repart-store-partition}
|
||||
|
||||
You can define a partition that only contains the Nix store and then mount it
|
||||
under `/nix/store`. Because the `/nix/store` part of the paths is already
|
||||
determined by the mount point, you have to set `stripNixStorePrefix = true;` so
|
||||
that the prefix is stripped from the paths before copying them into the image.
|
||||
|
||||
```nix
|
||||
fileSystems."/nix/store".device = "/dev/disk/by-partlabel/nix-store"
|
||||
|
||||
image.repart.partitions = {
|
||||
"store" = {
|
||||
storePaths = [ config.system.build.toplevel ];
|
||||
stripNixStorePrefix = true;
|
||||
repartConfig = {
|
||||
Type = "linux-generic";
|
||||
Label = "nix-store";
|
||||
...
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## Appliance Image {#sec-image-repart-appliance}
|
||||
|
||||
The `image/repart.nix` module can also be used to build self-contained [software
|
||||
appliances][].
|
||||
|
||||
[software appliances]: https://en.wikipedia.org/wiki/Software_appliance
|
||||
|
||||
The generation based update mechanism of NixOS is not suited for appliances.
|
||||
Updates of appliances are usually either performed by replacing the entire
|
||||
image with a new one or by updating partitions via an A/B scheme. See the
|
||||
[Chrome OS update process][chrome-os-update] for an example of how to achieve
|
||||
this. The appliance image built in the following example does not contain a
|
||||
`configuration.nix` and thus you will not be able to call `nixos-rebuild` from
|
||||
this system.
|
||||
|
||||
[chrome-os-update]: https://chromium.googlesource.com/aosp/platform/system/update_engine/+/HEAD/README.md
|
||||
|
||||
```nix
|
||||
let
|
||||
pkgs = import <nixpkgs> { };
|
||||
efiArch = pkgs.stdenv.hostPlatform.efiArch;
|
||||
in
|
||||
(pkgs.nixos [
|
||||
({ config, lib, pkgs, modulesPath, ... }: {
|
||||
|
||||
imports = [ "${modulesPath}/image/repart.nix" ];
|
||||
|
||||
boot.loader.grub.enable = false;
|
||||
|
||||
fileSystems."/".device = "/dev/disk/by-label/nixos";
|
||||
|
||||
image.repart = {
|
||||
name = "image";
|
||||
partitions = {
|
||||
"esp" = {
|
||||
contents = {
|
||||
"/EFI/BOOT/BOOT${lib.toUpper efiArch}.EFI".source =
|
||||
"${pkgs.systemd}/lib/systemd/boot/efi/systemd-boot${efiArch}.efi";
|
||||
|
||||
"/loader/entries/nixos.conf".source = pkgs.writeText "nixos.conf" ''
|
||||
title NixOS
|
||||
linux /EFI/nixos/kernel.efi
|
||||
initrd /EFI/nixos/initrd.efi
|
||||
options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
|
||||
'';
|
||||
|
||||
"/EFI/nixos/kernel.efi".source =
|
||||
"${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}";
|
||||
|
||||
"/EFI/nixos/initrd.efi".source =
|
||||
"${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
|
||||
};
|
||||
repartConfig = {
|
||||
Type = "esp";
|
||||
Format = "vfat";
|
||||
SizeMinBytes = "96M";
|
||||
};
|
||||
};
|
||||
"root" = {
|
||||
storePaths = [ config.system.build.toplevel ];
|
||||
repartConfig = {
|
||||
Type = "root";
|
||||
Format = "ext4";
|
||||
Label = "nixos";
|
||||
Minimize = "guess";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
})
|
||||
]).image
|
||||
```
|
207
nixos/modules/image/repart.nix
Normal file
207
nixos/modules/image/repart.nix
Normal file
|
@ -0,0 +1,207 @@
|
|||
# This module exposes options to build a disk image with a GUID Partition Table
|
||||
# (GPT). It uses systemd-repart to build the image.
|
||||
|
||||
{ config, pkgs, lib, utils, ... }:
|
||||
|
||||
let
|
||||
cfg = config.image.repart;
|
||||
|
||||
partitionOptions = {
|
||||
options = {
|
||||
storePaths = lib.mkOption {
|
||||
type = with lib.types; listOf path;
|
||||
default = [ ];
|
||||
description = lib.mdDoc "The store paths to include in the partition.";
|
||||
};
|
||||
|
||||
stripNixStorePrefix = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc ''
|
||||
Whether to strip `/nix/store/` from the store paths. This is useful
|
||||
when you want to build a partition that only contains store paths and
|
||||
is mounted under `/nix/store`.
|
||||
'';
|
||||
};
|
||||
|
||||
contents = lib.mkOption {
|
||||
type = with lib.types; attrsOf (submodule {
|
||||
options = {
|
||||
source = lib.mkOption {
|
||||
type = types.path;
|
||||
description = lib.mdDoc "Path of the source file.";
|
||||
};
|
||||
};
|
||||
});
|
||||
default = { };
|
||||
example = lib.literalExpression '' {
|
||||
"/EFI/BOOT/BOOTX64.EFI".source =
|
||||
"''${pkgs.systemd}/lib/systemd/boot/efi/systemd-bootx64.efi";
|
||||
|
||||
"/loader/entries/nixos.conf".source = systemdBootEntry;
|
||||
}
|
||||
'';
|
||||
description = lib.mdDoc "The contents to end up in the filesystem image.";
|
||||
};
|
||||
|
||||
repartConfig = lib.mkOption {
|
||||
type = with lib.types; attrsOf (oneOf [ str int bool ]);
|
||||
example = {
|
||||
Type = "home";
|
||||
SizeMinBytes = "512M";
|
||||
SizeMaxBytes = "2G";
|
||||
};
|
||||
description = lib.mdDoc ''
|
||||
Specify the repart options for a partiton as a structural setting.
|
||||
See <https://www.freedesktop.org/software/systemd/man/repart.d.html>
|
||||
for all available options.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
options.image.repart = {
|
||||
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc "The name of the image.";
|
||||
};
|
||||
|
||||
seed = lib.mkOption {
|
||||
type = with lib.types; nullOr str;
|
||||
# Generated with `uuidgen`. Random but fixed to improve reproducibility.
|
||||
default = "0867da16-f251-457d-a9e8-c31f9a3c220b";
|
||||
description = lib.mdDoc ''
|
||||
A UUID to use as a seed. You can set this to `null` to explicitly
|
||||
randomize the partition UUIDs.
|
||||
'';
|
||||
};
|
||||
|
||||
split = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc ''
|
||||
Enables generation of split artifacts from partitions. If enabled, for
|
||||
each partition with SplitName= set, a separate output file containing
|
||||
just the contents of that partition is generated.
|
||||
'';
|
||||
};
|
||||
|
||||
partitions = lib.mkOption {
|
||||
type = with lib.types; attrsOf (submodule partitionOptions);
|
||||
default = { };
|
||||
example = lib.literalExpression '' {
|
||||
"10-esp" = {
|
||||
contents = {
|
||||
"/EFI/BOOT/BOOTX64.EFI".source =
|
||||
"''${pkgs.systemd}/lib/systemd/boot/efi/systemd-bootx64.efi";
|
||||
}
|
||||
repartConfig = {
|
||||
Type = "esp";
|
||||
Format = "fat";
|
||||
};
|
||||
};
|
||||
"20-root" = {
|
||||
storePaths = [ config.system.build.toplevel ];
|
||||
repartConfig = {
|
||||
Type = "root";
|
||||
Format = "ext4";
|
||||
Minimize = "guess";
|
||||
};
|
||||
};
|
||||
};
|
||||
'';
|
||||
description = lib.mdDoc ''
|
||||
Specify partitions as a set of the names of the partitions with their
|
||||
configuration as the key.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = {
|
||||
|
||||
system.build.image =
|
||||
let
|
||||
fileSystemToolMapping = with pkgs; {
|
||||
"vfat" = [ dosfstools mtools ];
|
||||
"ext4" = [ e2fsprogs.bin ];
|
||||
"squashfs" = [ squashfsTools ];
|
||||
"erofs" = [ erofs-utils ];
|
||||
"btrfs" = [ btrfs-progs ];
|
||||
"xfs" = [ xfsprogs ];
|
||||
};
|
||||
|
||||
fileSystems = lib.filter
|
||||
(f: f != null)
|
||||
(lib.mapAttrsToList (_n: v: v.repartConfig.Format or null) cfg.partitions);
|
||||
|
||||
fileSystemTools = builtins.concatMap (f: fileSystemToolMapping."${f}") fileSystems;
|
||||
|
||||
|
||||
makeClosure = paths: pkgs.closureInfo { rootPaths = paths; };
|
||||
|
||||
# Add the closure of the provided Nix store paths to cfg.partitions so
|
||||
# that amend-repart-definitions.py can read it.
|
||||
addClosure = _name: partitionConfig: partitionConfig // (
|
||||
lib.optionalAttrs
|
||||
(partitionConfig.storePaths or [ ] != [ ])
|
||||
{ closure = "${makeClosure partitionConfig.storePaths}/store-paths"; }
|
||||
);
|
||||
|
||||
|
||||
finalPartitions = lib.mapAttrs addClosure cfg.partitions;
|
||||
|
||||
|
||||
amendRepartDefinitions = pkgs.runCommand "amend-repart-definitions.py"
|
||||
{
|
||||
nativeBuildInputs = with pkgs; [ black ruff mypy ];
|
||||
buildInputs = [ pkgs.python3 ];
|
||||
} ''
|
||||
install ${./amend-repart-definitions.py} $out
|
||||
patchShebangs --host $out
|
||||
|
||||
black --check --diff $out
|
||||
ruff --line-length 88 $out
|
||||
mypy --strict $out
|
||||
'';
|
||||
|
||||
format = pkgs.formats.ini { };
|
||||
|
||||
definitionsDirectory = utils.systemdUtils.lib.definitions
|
||||
"repart.d"
|
||||
format
|
||||
(lib.mapAttrs (_n: v: { Partition = v.repartConfig; }) finalPartitions);
|
||||
|
||||
partitions = pkgs.writeText "partitions.json" (builtins.toJSON finalPartitions);
|
||||
in
|
||||
pkgs.runCommand cfg.name
|
||||
{
|
||||
nativeBuildInputs = with pkgs; [
|
||||
fakeroot
|
||||
systemd
|
||||
] ++ fileSystemTools;
|
||||
} ''
|
||||
amendedRepartDefinitions=$(${amendRepartDefinitions} ${partitions} ${definitionsDirectory})
|
||||
|
||||
mkdir -p $out
|
||||
cd $out
|
||||
|
||||
fakeroot systemd-repart \
|
||||
--dry-run=no \
|
||||
--empty=create \
|
||||
--size=auto \
|
||||
--seed="${cfg.seed}" \
|
||||
--definitions="$amendedRepartDefinitions" \
|
||||
--split="${lib.boolToString cfg.split}" \
|
||||
image.raw
|
||||
'';
|
||||
|
||||
meta = {
|
||||
maintainers = with lib.maintainers; [ nikstur ];
|
||||
doc = ./repart.md;
|
||||
};
|
||||
|
||||
};
|
||||
}
|
|
@ -39,9 +39,7 @@ with lib;
|
|||
|
||||
# !!! Hack - attributes expected by other modules.
|
||||
environment.systemPackages = [ pkgs.grub2_efi ]
|
||||
++ (if pkgs.stdenv.hostPlatform.system == "aarch64-linux"
|
||||
then []
|
||||
else [ pkgs.grub2 pkgs.syslinux ]);
|
||||
++ (lib.optionals (pkgs.stdenv.hostPlatform.system != "aarch64-linux") [pkgs.grub2 pkgs.syslinux]);
|
||||
|
||||
fileSystems."/" = mkImageMediaOverride
|
||||
{ fsType = "tmpfs";
|
||||
|
|
|
@ -381,6 +381,7 @@ sub in {
|
|||
|
||||
my $fileSystems;
|
||||
my %fsByDev;
|
||||
my $useSwraid = 0;
|
||||
foreach my $fs (read_file("/proc/self/mountinfo")) {
|
||||
chomp $fs;
|
||||
my @fields = split / /, $fs;
|
||||
|
@ -510,8 +511,8 @@ EOF
|
|||
# boot.initrd.luks.devices entry.
|
||||
if (-e $device) {
|
||||
my $deviceName = basename(abs_path($device));
|
||||
if (-e "/sys/class/block/$deviceName"
|
||||
&& read_file("/sys/class/block/$deviceName/dm/uuid", err_mode => 'quiet') =~ /^CRYPT-LUKS/)
|
||||
my $dmUuid = read_file("/sys/class/block/$deviceName/dm/uuid", err_mode => 'quiet');
|
||||
if ($dmUuid =~ /^CRYPT-LUKS/)
|
||||
{
|
||||
my @slaves = glob("/sys/class/block/$deviceName/slaves/*");
|
||||
if (scalar @slaves == 1) {
|
||||
|
@ -527,8 +528,14 @@ EOF
|
|||
}
|
||||
}
|
||||
}
|
||||
if (-e "/sys/class/block/$deviceName/md/uuid") {
|
||||
$useSwraid = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($useSwraid) {
|
||||
push @attrs, "boot.swraid.enable = true;\n\n";
|
||||
}
|
||||
|
||||
|
||||
# Generate the hardware configuration file.
|
||||
|
|
|
@ -42,10 +42,7 @@ let
|
|||
xserverEnabled = config.services.xserver.enable;
|
||||
};
|
||||
|
||||
nixos-option =
|
||||
if lib.versionAtLeast (lib.getVersion config.nix.package) "2.4pre"
|
||||
then null
|
||||
else pkgs.nixos-option;
|
||||
inherit (pkgs) nixos-option;
|
||||
|
||||
nixos-version = makeProg {
|
||||
name = "nixos-version";
|
||||
|
@ -129,7 +126,7 @@ in
|
|||
# your system. Help is available in the configuration.nix(5) man page
|
||||
# and in the NixOS manual (accessible by running `nixos-help`).
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports =
|
||||
|
@ -232,9 +229,10 @@ in
|
|||
nixos-install
|
||||
nixos-rebuild
|
||||
nixos-generate-config
|
||||
nixos-option
|
||||
nixos-version
|
||||
nixos-enter
|
||||
] ++ lib.optional (nixos-option != null) nixos-option;
|
||||
];
|
||||
|
||||
documentation.man.man-db.skipPackages = [ nixos-version ];
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
./config/debug-info.nix
|
||||
./config/fonts/fontconfig.nix
|
||||
./config/fonts/fontdir.nix
|
||||
./config/fonts/fonts.nix
|
||||
./config/fonts/ghostscript.nix
|
||||
./config/fonts/packages.nix
|
||||
./config/gnu.nix
|
||||
./config/gtk/gtk-icon-cache.nix
|
||||
./config/i18n.nix
|
||||
|
@ -93,8 +93,8 @@
|
|||
./hardware/tuxedo-keyboard.nix
|
||||
./hardware/ubertooth.nix
|
||||
./hardware/uinput.nix
|
||||
./hardware/usb-modeswitch.nix
|
||||
./hardware/usb-storage.nix
|
||||
./hardware/usb-wwan.nix
|
||||
./hardware/video/amdgpu-pro.nix
|
||||
./hardware/video/bumblebee.nix
|
||||
./hardware/video/capture/mwprocapture.nix
|
||||
|
@ -160,6 +160,7 @@
|
|||
./programs/darling.nix
|
||||
./programs/dconf.nix
|
||||
./programs/digitalbitbox/default.nix
|
||||
./programs/direnv.nix
|
||||
./programs/dmrconfig.nix
|
||||
./programs/droidcam.nix
|
||||
./programs/environment.nix
|
||||
|
@ -221,6 +222,7 @@
|
|||
./programs/noisetorch.nix
|
||||
./programs/npm.nix
|
||||
./programs/oblogout.nix
|
||||
./programs/oddjobd.nix
|
||||
./programs/openvpn3.nix
|
||||
./programs/pantheon-tweaks.nix
|
||||
./programs/partition-manager.nix
|
||||
|
@ -262,6 +264,7 @@
|
|||
./programs/wayland/river.nix
|
||||
./programs/wayland/sway.nix
|
||||
./programs/wayland/waybar.nix
|
||||
./programs/wayland/wayfire.nix
|
||||
./programs/weylus.nix
|
||||
./programs/wireshark.nix
|
||||
./programs/xastir.nix
|
||||
|
@ -416,6 +419,7 @@
|
|||
./services/databases/neo4j.nix
|
||||
./services/databases/openldap.nix
|
||||
./services/databases/opentsdb.nix
|
||||
./services/databases/pgbouncer.nix
|
||||
./services/databases/pgmanage.nix
|
||||
./services/databases/postgresql.nix
|
||||
./services/databases/redis.nix
|
||||
|
@ -534,6 +538,7 @@
|
|||
./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
|
||||
./services/home-automation/home-assistant.nix
|
||||
|
@ -597,6 +602,7 @@
|
|||
./services/matrix/mjolnir.nix
|
||||
./services/matrix/mx-puppet-discord.nix
|
||||
./services/matrix/pantalaimon.nix
|
||||
./services/matrix/matrix-sliding-sync.nix
|
||||
./services/matrix/synapse.nix
|
||||
./services/misc/airsonic.nix
|
||||
./services/misc/ananicy.nix
|
||||
|
@ -607,6 +613,7 @@
|
|||
./services/misc/autorandr.nix
|
||||
./services/misc/autosuspend.nix
|
||||
./services/misc/bazarr.nix
|
||||
./services/misc/bcg.nix
|
||||
./services/misc/beanstalkd.nix
|
||||
./services/misc/bees.nix
|
||||
./services/misc/bepasty.nix
|
||||
|
@ -630,6 +637,7 @@
|
|||
./services/misc/etcd.nix
|
||||
./services/misc/etebase-server.nix
|
||||
./services/misc/etesync-dav.nix
|
||||
./services/misc/evdevremapkeys.nix
|
||||
./services/misc/felix.nix
|
||||
./services/misc/freeswitch.nix
|
||||
./services/misc/fstrim.nix
|
||||
|
@ -645,6 +653,7 @@
|
|||
./services/misc/greenclip.nix
|
||||
./services/misc/headphones.nix
|
||||
./services/misc/heisenbridge.nix
|
||||
./services/misc/homepage-dashboard.nix
|
||||
./services/misc/ihaskell.nix
|
||||
./services/misc/input-remapper.nix
|
||||
./services/misc/irkerd.nix
|
||||
|
@ -663,6 +672,7 @@
|
|||
./services/misc/mediatomb.nix
|
||||
./services/misc/metabase.nix
|
||||
./services/misc/moonraker.nix
|
||||
./services/misc/mqtt2influxdb.nix
|
||||
./services/misc/n8n.nix
|
||||
./services/misc/nitter.nix
|
||||
./services/misc/nix-gc.nix
|
||||
|
@ -759,6 +769,7 @@
|
|||
./services/monitoring/nagios.nix
|
||||
./services/monitoring/netdata.nix
|
||||
./services/monitoring/opentelemetry-collector.nix
|
||||
./services/monitoring/osquery.nix
|
||||
./services/monitoring/parsedmarc.nix
|
||||
./services/monitoring/prometheus/alertmanager-irc-relay.nix
|
||||
./services/monitoring/prometheus/alertmanager.nix
|
||||
|
@ -1057,6 +1068,7 @@
|
|||
./services/networking/tox-node.nix
|
||||
./services/networking/toxvpn.nix
|
||||
./services/networking/trickster.nix
|
||||
./services/networking/trust-dns.nix
|
||||
./services/networking/tvheadend.nix
|
||||
./services/networking/twingate.nix
|
||||
./services/networking/ucarp.nix
|
||||
|
@ -1100,6 +1112,7 @@
|
|||
./services/search/meilisearch.nix
|
||||
./services/search/opensearch.nix
|
||||
./services/search/qdrant.nix
|
||||
./services/search/typesense.nix
|
||||
./services/security/aesmd.nix
|
||||
./services/security/authelia.nix
|
||||
./services/security/certmgr.nix
|
||||
|
@ -1107,6 +1120,7 @@
|
|||
./services/security/clamav.nix
|
||||
./services/security/endlessh-go.nix
|
||||
./services/security/endlessh.nix
|
||||
./services/security/esdm.nix
|
||||
./services/security/fail2ban.nix
|
||||
./services/security/fprintd.nix
|
||||
./services/security/haka.nix
|
||||
|
@ -1138,6 +1152,7 @@
|
|||
./services/security/vaultwarden/default.nix
|
||||
./services/security/yubikey-agent.nix
|
||||
./services/system/automatic-timezoned.nix
|
||||
./services/system/bpftune.nix
|
||||
./services/system/cachix-agent/default.nix
|
||||
./services/system/cachix-watch-store.nix
|
||||
./services/system/cloud-init.nix
|
||||
|
@ -1251,6 +1266,7 @@
|
|||
./services/web-apps/rss-bridge.nix
|
||||
./services/web-apps/selfoss.nix
|
||||
./services/web-apps/shiori.nix
|
||||
./services/web-apps/slskd.nix
|
||||
./services/web-apps/snipe-it.nix
|
||||
./services/web-apps/sogo.nix
|
||||
./services/web-apps/trilium.nix
|
||||
|
@ -1383,6 +1399,7 @@
|
|||
./system/boot/systemd/oomd.nix
|
||||
./system/boot/systemd/repart.nix
|
||||
./system/boot/systemd/shutdown.nix
|
||||
./system/boot/systemd/sysupdate.nix
|
||||
./system/boot/systemd/tmpfiles.nix
|
||||
./system/boot/systemd/user.nix
|
||||
./system/boot/systemd/userdbd.nix
|
||||
|
|
|
@ -106,6 +106,8 @@ with lib;
|
|||
systemdStage1Network
|
||||
];
|
||||
|
||||
boot.swraid.enable = true;
|
||||
|
||||
# Show all debug messages from the kernel but don't log refused packets
|
||||
# because we have the firewall enabled. This makes installs from the
|
||||
# console less cumbersome if the machine has a public IP.
|
||||
|
|
|
@ -21,7 +21,8 @@ in
|
|||
../virtualisation/nixos-containers.nix
|
||||
../services/x11/desktop-managers/xterm.nix
|
||||
];
|
||||
config = { };
|
||||
# swraid's default depends on stateVersion
|
||||
config.boot.swraid.enable = false;
|
||||
options.boot.isContainer = lib.mkOption { default = false; internal = true; };
|
||||
}
|
||||
];
|
||||
|
|
|
@ -123,8 +123,8 @@ in
|
|||
boot.extraModulePackages = [ (lib.mkIf cfg.netatop.enable cfg.netatop.package) ];
|
||||
systemd =
|
||||
let
|
||||
mkSystemd = type: cond: name: restartTriggers: {
|
||||
${name} = lib.mkIf cond {
|
||||
mkSystemd = type: name: restartTriggers: {
|
||||
${name} = {
|
||||
inherit restartTriggers;
|
||||
wantedBy = [ (if type == "services" then "multi-user.target" else if type == "timers" then "timers.target" else null) ];
|
||||
};
|
||||
|
@ -134,42 +134,44 @@ in
|
|||
in
|
||||
{
|
||||
packages = [ atop (lib.mkIf cfg.netatop.enable cfg.netatop.package) ];
|
||||
services =
|
||||
mkService cfg.atopService.enable "atop" [ atop ]
|
||||
// lib.mkIf cfg.atopService.enable {
|
||||
# always convert logs to newer version first
|
||||
# XXX might trigger TimeoutStart but restarting atop.service will
|
||||
# convert remainings logs and start eventually
|
||||
atop.serviceConfig.ExecStartPre = pkgs.writeShellScript "atop-update-log-format" ''
|
||||
set -e -u
|
||||
shopt -s nullglob
|
||||
for logfile in "$LOGPATH"/atop_*
|
||||
do
|
||||
${atop}/bin/atopconvert "$logfile" "$logfile".new
|
||||
# only replace old file if version was upgraded to avoid
|
||||
# false positives for atop-rotate.service
|
||||
if ! ${pkgs.diffutils}/bin/cmp -s "$logfile" "$logfile".new
|
||||
then
|
||||
${pkgs.coreutils}/bin/mv -v -f "$logfile".new "$logfile"
|
||||
else
|
||||
${pkgs.coreutils}/bin/rm -f "$logfile".new
|
||||
fi
|
||||
done
|
||||
'';
|
||||
}
|
||||
// mkService cfg.atopacctService.enable "atopacct" [ atop ]
|
||||
// mkService cfg.netatop.enable "netatop" [ cfg.netatop.package ]
|
||||
// mkService cfg.atopgpu.enable "atopgpu" [ atop ];
|
||||
timers = mkTimer cfg.atopRotateTimer.enable "atop-rotate" [ atop ];
|
||||
services = lib.mkMerge [
|
||||
(lib.mkIf cfg.atopService.enable (lib.recursiveUpdate
|
||||
(mkService "atop" [ atop ])
|
||||
{
|
||||
# always convert logs to newer version first
|
||||
# XXX might trigger TimeoutStart but restarting atop.service will
|
||||
# convert remainings logs and start eventually
|
||||
atop.preStart = ''
|
||||
set -e -u
|
||||
shopt -s nullglob
|
||||
for logfile in "$LOGPATH"/atop_*
|
||||
do
|
||||
${atop}/bin/atopconvert "$logfile" "$logfile".new
|
||||
# only replace old file if version was upgraded to avoid
|
||||
# false positives for atop-rotate.service
|
||||
if ! ${pkgs.diffutils}/bin/cmp -s "$logfile" "$logfile".new
|
||||
then
|
||||
${pkgs.coreutils}/bin/mv -v -f "$logfile".new "$logfile"
|
||||
else
|
||||
${pkgs.coreutils}/bin/rm -f "$logfile".new
|
||||
fi
|
||||
done
|
||||
'';
|
||||
}))
|
||||
(lib.mkIf cfg.atopacctService.enable (mkService "atopacct" [ atop ]))
|
||||
(lib.mkIf cfg.netatop.enable (mkService "netatop" [ cfg.netatop.package ]))
|
||||
(lib.mkIf cfg.atopgpu.enable (mkService "atopgpu" [ atop ]))
|
||||
];
|
||||
timers = lib.mkIf cfg.atopRotateTimer.enable (mkTimer "atop-rotate" [ atop ]);
|
||||
};
|
||||
|
||||
security.wrappers = lib.mkIf cfg.setuidWrapper.enable {
|
||||
atop =
|
||||
{ setuid = true;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
source = "${atop}/bin/atop";
|
||||
};
|
||||
atop = {
|
||||
setuid = true;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
source = "${atop}/bin/atop";
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
|
|
147
nixos/modules/programs/direnv.nix
Normal file
147
nixos/modules/programs/direnv.nix
Normal file
|
@ -0,0 +1,147 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}: let
|
||||
cfg = config.programs.direnv;
|
||||
in {
|
||||
options.programs.direnv = {
|
||||
|
||||
enable = lib.mkEnableOption (lib.mdDoc ''
|
||||
direnv integration. Takes care of both installation and
|
||||
setting up the sourcing of the shell. Additionally enables nix-direnv
|
||||
integration. Note that you need to logout and login for this change to apply.
|
||||
'');
|
||||
|
||||
package = lib.mkPackageOptionMD pkgs "direnv" {};
|
||||
|
||||
direnvrcExtra = lib.mkOption {
|
||||
type = lib.types.lines;
|
||||
default = "";
|
||||
example = ''
|
||||
export FOO="foo"
|
||||
echo "loaded direnv!"
|
||||
'';
|
||||
description = lib.mdDoc ''
|
||||
Extra lines to append to the sourced direnvrc
|
||||
'';
|
||||
};
|
||||
|
||||
silent = lib.mkEnableOption (lib.mdDoc ''
|
||||
the hiding of direnv logging
|
||||
'');
|
||||
|
||||
persistDerivations =
|
||||
(lib.mkEnableOption (lib.mdDoc ''
|
||||
setting keep-derivations and keep-outputs to true
|
||||
to prevent shells from getting garbage collected
|
||||
''))
|
||||
// {
|
||||
default = true;
|
||||
};
|
||||
|
||||
loadInNixShell =
|
||||
lib.mkEnableOption (lib.mdDoc ''
|
||||
loading direnv in `nix-shell` `nix shell` or `nix develop`
|
||||
'')
|
||||
// {
|
||||
default = true;
|
||||
};
|
||||
|
||||
nix-direnv = {
|
||||
enable =
|
||||
(lib.mkEnableOption (lib.mdDoc ''
|
||||
a faster, persistent implementation of use_nix and use_flake, to replace the built-in one
|
||||
''))
|
||||
// {
|
||||
default = true;
|
||||
};
|
||||
|
||||
package = lib.mkPackageOptionMD pkgs "nix-direnv" {};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
programs = {
|
||||
zsh.interactiveShellInit = ''
|
||||
if ${lib.boolToString cfg.loadInNixShell} || printenv PATH | grep -vqc '/nix/store'; then
|
||||
eval "$(${lib.getExe cfg.package} hook zsh)"
|
||||
fi
|
||||
'';
|
||||
|
||||
#$NIX_GCROOT for "nix develop" https://github.com/NixOS/nix/blob/6db66ebfc55769edd0c6bc70fcbd76246d4d26e0/src/nix/develop.cc#L530
|
||||
#$IN_NIX_SHELL for "nix-shell"
|
||||
bash.interactiveShellInit = ''
|
||||
if ${lib.boolToString cfg.loadInNixShell} || [ -z "$IN_NIX_SHELL$NIX_GCROOT$(printenv PATH | grep '/nix/store')" ] ; then
|
||||
eval "$(${lib.getExe cfg.package} hook bash)"
|
||||
fi
|
||||
'';
|
||||
|
||||
fish.interactiveShellInit = ''
|
||||
if ${lib.boolToString cfg.loadInNixShell};
|
||||
or printenv PATH | grep -vqc '/nix/store';
|
||||
${lib.getExe cfg.package} hook fish | source
|
||||
end
|
||||
'';
|
||||
};
|
||||
|
||||
nix.settings = lib.mkIf cfg.persistDerivations {
|
||||
keep-outputs = true;
|
||||
keep-derivations = true;
|
||||
};
|
||||
|
||||
environment = {
|
||||
systemPackages =
|
||||
if cfg.loadInNixShell then [cfg.package]
|
||||
else [
|
||||
#direnv has a fish library which sources direnv for some reason
|
||||
(cfg.package.overrideAttrs (old: {
|
||||
installPhase =
|
||||
(old.installPhase or "")
|
||||
+ ''
|
||||
rm -rf $out/share/fish
|
||||
'';
|
||||
}))
|
||||
];
|
||||
|
||||
variables = {
|
||||
DIRENV_CONFIG = "/etc/direnv";
|
||||
DIRENV_LOG_FORMAT = lib.mkIf cfg.silent "";
|
||||
};
|
||||
|
||||
etc = {
|
||||
"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 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
|
||||
'';
|
||||
|
||||
"direnv/lib/zz-user.sh".text = ''
|
||||
direnv_config_dir_home="''${DIRENV_CONFIG_HOME:-''${XDG_CONFIG_HOME:-$HOME/.config}/direnv}"
|
||||
|
||||
for lib in "$direnv_config_dir_home/lib/"*.sh; do
|
||||
source "$lib"
|
||||
done
|
||||
|
||||
unset direnv_config_dir_home
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -233,7 +233,6 @@ in
|
|||
nixpkgs.config.firefox = {
|
||||
enableBrowserpass = nmh.browserpass;
|
||||
enableBukubrow = nmh.bukubrow;
|
||||
enableEUWebID = nmh.euwebid;
|
||||
enableTridactylNative = nmh.tridactyl;
|
||||
enableUgetIntegrator = nmh.ugetIntegrator;
|
||||
enableFXCastBridge = nmh.fxCast;
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
with lib; let
|
||||
cfg = config.programs.hyprland;
|
||||
|
||||
defaultHyprlandPackage = pkgs.hyprland.override {
|
||||
enableXWayland = cfg.xwayland.enable;
|
||||
hidpiXWayland = cfg.xwayland.hidpi;
|
||||
nvidiaPatches = cfg.nvidiaPatches;
|
||||
finalPortalPackage = cfg.portalPackage.override {
|
||||
hyprland-share-picker = pkgs.hyprland-share-picker.override {
|
||||
hyprland = cfg.finalPackage;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
|
@ -25,24 +25,25 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
package = mkOption {
|
||||
type = types.path;
|
||||
default = defaultHyprlandPackage;
|
||||
defaultText = literalExpression ''
|
||||
pkgs.hyprland.override {
|
||||
enableXWayland = config.programs.hyprland.xwayland.enable;
|
||||
hidpiXWayland = config.programs.hyprland.xwayland.hidpi;
|
||||
nvidiaPatches = config.programs.hyprland.nvidiaPatches;
|
||||
}
|
||||
'';
|
||||
example = literalExpression "<Hyprland flake>.packages.<system>.default";
|
||||
package = mkPackageOptionMD pkgs "hyprland" { };
|
||||
|
||||
finalPackage = mkOption {
|
||||
type = types.package;
|
||||
readOnly = true;
|
||||
default = cfg.package.override {
|
||||
enableXWayland = cfg.xwayland.enable;
|
||||
hidpiXWayland = cfg.xwayland.hidpi;
|
||||
nvidiaPatches = cfg.nvidiaPatches;
|
||||
};
|
||||
defaultText = literalExpression
|
||||
"`wayland.windowManager.hyprland.package` with applied configuration";
|
||||
description = mdDoc ''
|
||||
The Hyprland package to use.
|
||||
Setting this option will make {option}`programs.hyprland.xwayland` and
|
||||
{option}`programs.hyprland.nvidiaPatches` not work.
|
||||
The Hyprland package after applying configuration.
|
||||
'';
|
||||
};
|
||||
|
||||
portalPackage = mkPackageOptionMD pkgs "xdg-desktop-portal-hyprland" { };
|
||||
|
||||
xwayland = {
|
||||
enable = mkEnableOption (mdDoc "XWayland") // { default = true; };
|
||||
hidpi = mkEnableOption null // {
|
||||
|
@ -57,9 +58,9 @@ in
|
|||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
environment.systemPackages = [ cfg.finalPackage ];
|
||||
|
||||
fonts.enableDefaultFonts = mkDefault true;
|
||||
fonts.enableDefaultPackages = mkDefault true;
|
||||
hardware.opengl.enable = mkDefault true;
|
||||
|
||||
programs = {
|
||||
|
@ -69,13 +70,11 @@ in
|
|||
|
||||
security.polkit.enable = true;
|
||||
|
||||
services.xserver.displayManager.sessionPackages = [ cfg.package ];
|
||||
services.xserver.displayManager.sessionPackages = [ cfg.finalPackage ];
|
||||
|
||||
xdg.portal = {
|
||||
enable = mkDefault true;
|
||||
extraPortals = [
|
||||
pkgs.xdg-desktop-portal-hyprland
|
||||
];
|
||||
extraPortals = [ finalPortalPackage ];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ in {
|
|||
};
|
||||
|
||||
hardware.opengl.enable = lib.mkDefault true;
|
||||
fonts.enableDefaultFonts = lib.mkDefault true;
|
||||
fonts.enableDefaultPackages = lib.mkDefault true;
|
||||
programs.dconf.enable = lib.mkDefault true;
|
||||
programs.xwayland.enable = lib.mkDefault true;
|
||||
|
||||
|
|
28
nixos/modules/programs/oddjobd.nix
Normal file
28
nixos/modules/programs/oddjobd.nix
Normal file
|
@ -0,0 +1,28 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
cfg = config.programs.oddjobd;
|
||||
in
|
||||
{
|
||||
options.programs.oddjobd = {
|
||||
enable = lib.mkEnableOption "oddjob";
|
||||
package = lib.mkPackageOption pkgs "oddjob" {};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.packages = [ cfg.package ];
|
||||
|
||||
systemd.services.oddjobd = {
|
||||
wantedBy = [ "multi-user.target"];
|
||||
after = [ "network.target"];
|
||||
description = "DBUS Odd-job Daemon";
|
||||
enable = true;
|
||||
documentation = [ "man:oddjobd(8)" "man:oddjobd.conf(5)" ];
|
||||
serviceConfig = {
|
||||
Type = "dbus";
|
||||
BusName = "org.freedesktop.oddjob";
|
||||
ExecStart = "${lib.getExe cfg.package}/bin/oddjobd";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
48
nixos/modules/programs/wayland/wayfire.nix
Normal file
48
nixos/modules/programs/wayland/wayfire.nix
Normal file
|
@ -0,0 +1,48 @@
|
|||
{ config, lib, pkgs, ...}:
|
||||
let
|
||||
cfg = config.programs.wayfire;
|
||||
in
|
||||
{
|
||||
meta.maintainers = with lib.maintainers; [ rewine ];
|
||||
|
||||
options.programs.wayfire = {
|
||||
enable = lib.mkEnableOption (lib.mdDoc "Wayfire, a wayland compositor based on wlroots.");
|
||||
|
||||
package = lib.mkPackageOptionMD pkgs "wayfire" { };
|
||||
|
||||
plugins = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.package;
|
||||
default = with pkgs.wayfirePlugins; [ wcm wf-shell ];
|
||||
defaultText = lib.literalExpression "with pkgs.wayfirePlugins; [ wcm wf-shell ]";
|
||||
example = lib.literalExpression ''
|
||||
with pkgs.wayfirePlugins; [
|
||||
wcm
|
||||
wf-shell
|
||||
wayfire-plugins-extra
|
||||
];
|
||||
'';
|
||||
description = lib.mdDoc ''
|
||||
Additional plugins to use with the wayfire window manager.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = let
|
||||
finalPackage = pkgs.wayfire-with-plugins.override {
|
||||
wayfire = cfg.package;
|
||||
plugins = cfg.plugins;
|
||||
};
|
||||
in
|
||||
lib.mkIf cfg.enable {
|
||||
environment.systemPackages = [
|
||||
finalPackage
|
||||
];
|
||||
|
||||
services.xserver.displayManager.sessionPackages = [ finalPackage ];
|
||||
|
||||
xdg.portal = {
|
||||
enable = lib.mkDefault true;
|
||||
wlr.enable = lib.mkDefault true;
|
||||
};
|
||||
};
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
};
|
||||
|
||||
hardware.opengl.enable = mkDefault true;
|
||||
fonts.enableDefaultFonts = mkDefault true;
|
||||
fonts.enableDefaultPackages = mkDefault true;
|
||||
|
||||
programs = {
|
||||
dconf.enable = mkDefault true;
|
||||
|
|
|
@ -72,7 +72,6 @@ in
|
|||
(mkRemovedOptionModule [ "services" "mesos" ] "The corresponding package was removed from nixpkgs.")
|
||||
(mkRemovedOptionModule [ "services" "moinmoin" ] "The corresponding package was removed from nixpkgs.")
|
||||
(mkRemovedOptionModule [ "services" "mwlib" ] "The corresponding package was removed from nixpkgs.")
|
||||
(mkRemovedOptionModule [ "services" "osquery" ] "The osquery module has been removed")
|
||||
(mkRemovedOptionModule [ "services" "pantheon" "files" ] ''
|
||||
This module was removed, please add pkgs.pantheon.elementary-files to environment.systemPackages directly.
|
||||
'')
|
||||
|
|
|
@ -44,5 +44,5 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
meta.maintainers = with maintainers; [ SuperSandro2000 ];
|
||||
meta.maintainers = with maintainers; [ ];
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ let
|
|||
}
|
||||
trap on_exit EXIT
|
||||
|
||||
archiveName="${if cfg.archiveBaseName == null then "" else cfg.archiveBaseName + "-"}$(date ${cfg.dateFormat})"
|
||||
archiveName="${optionalString (cfg.archiveBaseName != null) (cfg.archiveBaseName + "-")}$(date ${cfg.dateFormat})"
|
||||
archiveSuffix="${optionalString cfg.appendFailedSuffix ".failed"}"
|
||||
${cfg.preHook}
|
||||
'' + optionalString cfg.doInit ''
|
||||
|
|
|
@ -32,6 +32,8 @@ in
|
|||
services.tarsnap = {
|
||||
enable = mkEnableOption (lib.mdDoc "periodic tarsnap backups");
|
||||
|
||||
package = mkPackageOption pkgs "tarsnap" { };
|
||||
|
||||
keyfile = mkOption {
|
||||
type = types.str;
|
||||
default = "/root/tarsnap.key";
|
||||
|
@ -307,7 +309,7 @@ in
|
|||
requires = [ "network-online.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
|
||||
path = with pkgs; [ iputils tarsnap util-linux ];
|
||||
path = with pkgs; [ iputils gcfg.package util-linux ];
|
||||
|
||||
# In order for the persistent tarsnap timer to work reliably, we have to
|
||||
# make sure that the tarsnap server is reachable after systemd starts up
|
||||
|
@ -318,7 +320,7 @@ in
|
|||
'';
|
||||
|
||||
script = let
|
||||
tarsnap = ''tarsnap --configfile "/etc/tarsnap/${name}.conf"'';
|
||||
tarsnap = ''${lib.getExe gcfg.package} --configfile "/etc/tarsnap/${name}.conf"'';
|
||||
run = ''${tarsnap} -c -f "${name}-$(date +"%Y%m%d%H%M%S")" \
|
||||
${optionalString cfg.verbose "-v"} \
|
||||
${optionalString cfg.explicitSymlinks "-H"} \
|
||||
|
@ -355,10 +357,10 @@ in
|
|||
description = "Tarsnap restore '${name}'";
|
||||
requires = [ "network-online.target" ];
|
||||
|
||||
path = with pkgs; [ iputils tarsnap util-linux ];
|
||||
path = with pkgs; [ iputils gcfg.package util-linux ];
|
||||
|
||||
script = let
|
||||
tarsnap = ''tarsnap --configfile "/etc/tarsnap/${name}.conf"'';
|
||||
tarsnap = ''${lib.getExe gcfg.package} --configfile "/etc/tarsnap/${name}.conf"'';
|
||||
lastArchive = "$(${tarsnap} --list-archives | sort | tail -1)";
|
||||
run = ''${tarsnap} -x -f "${lastArchive}" ${optionalString cfg.verbose "-v"}'';
|
||||
cachedir = escapeShellArg cfg.cachedir;
|
||||
|
@ -402,6 +404,6 @@ in
|
|||
{ text = configFile name cfg;
|
||||
}) gcfg.archives;
|
||||
|
||||
environment.systemPackages = [ pkgs.tarsnap ];
|
||||
environment.systemPackages = [ gcfg.package ];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,9 +6,6 @@ let
|
|||
defaultGroup = "patroni";
|
||||
format = pkgs.formats.yaml { };
|
||||
|
||||
#boto doesn't support python 3.10 yet
|
||||
patroni = pkgs.patroni.override { pythonPackages = pkgs.python39Packages; };
|
||||
|
||||
configFileName = "patroni-${cfg.scope}-${cfg.name}.yaml";
|
||||
configFile = format.generate configFileName cfg.settings;
|
||||
in
|
||||
|
@ -224,7 +221,7 @@ in
|
|||
|
||||
script = ''
|
||||
${concatStringsSep "\n" (attrValues (mapAttrs (name: path: ''export ${name}="$(< ${escapeShellArg path})"'') cfg.environmentFiles))}
|
||||
exec ${patroni}/bin/patroni ${configFile}
|
||||
exec ${pkgs.patroni}/bin/patroni ${configFile}
|
||||
'';
|
||||
|
||||
serviceConfig = mkMerge [
|
||||
|
@ -252,7 +249,7 @@ in
|
|||
'';
|
||||
|
||||
environment.systemPackages = [
|
||||
patroni
|
||||
pkgs.patroni
|
||||
cfg.postgresqlPackage
|
||||
(mkIf cfg.raft pkgs.python310Packages.pysyncobj)
|
||||
];
|
||||
|
|
|
@ -1,64 +1,49 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.buildkite-agents;
|
||||
|
||||
mkHookOption = { name, description, example ? null }: {
|
||||
inherit name;
|
||||
value = mkOption {
|
||||
default = null;
|
||||
description = lib.mdDoc description;
|
||||
type = types.nullOr types.lines;
|
||||
} // (lib.optionalAttrs (example != null) { inherit example; });
|
||||
};
|
||||
mkHookOptions = hooks: listToAttrs (map mkHookOption hooks);
|
||||
|
||||
hooksDir = cfg: let
|
||||
mkHookEntry = name: value: ''
|
||||
cat > $out/${name} <<'EOF'
|
||||
#! ${pkgs.runtimeShell}
|
||||
set -e
|
||||
${value}
|
||||
EOF
|
||||
chmod 755 $out/${name}
|
||||
hooksDir = hooks:
|
||||
let
|
||||
mkHookEntry = name: text: ''
|
||||
ln --symbolic ${pkgs.writeShellApplication { inherit name text; }}/bin/${name} $out/${name}
|
||||
'';
|
||||
in
|
||||
pkgs.runCommandLocal "buildkite-agent-hooks" { } ''
|
||||
mkdir $out
|
||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList mkHookEntry hooks)}
|
||||
'';
|
||||
in pkgs.runCommand "buildkite-agent-hooks" { preferLocalBuild = true; } ''
|
||||
mkdir $out
|
||||
${concatStringsSep "\n" (mapAttrsToList mkHookEntry (filterAttrs (n: v: v != null) cfg.hooks))}
|
||||
'';
|
||||
|
||||
buildkiteOptions = { name ? "", config, ... }: {
|
||||
options = {
|
||||
enable = mkOption {
|
||||
enable = lib.mkOption {
|
||||
default = true;
|
||||
type = types.bool;
|
||||
type = lib.types.bool;
|
||||
description = lib.mdDoc "Whether to enable this buildkite agent";
|
||||
};
|
||||
|
||||
package = mkOption {
|
||||
package = lib.mkOption {
|
||||
default = pkgs.buildkite-agent;
|
||||
defaultText = literalExpression "pkgs.buildkite-agent";
|
||||
defaultText = lib.literalExpression "pkgs.buildkite-agent";
|
||||
description = lib.mdDoc "Which buildkite-agent derivation to use";
|
||||
type = types.package;
|
||||
type = lib.types.package;
|
||||
};
|
||||
|
||||
dataDir = mkOption {
|
||||
dataDir = lib.mkOption {
|
||||
default = "/var/lib/buildkite-agent-${name}";
|
||||
description = lib.mdDoc "The workdir for the agent";
|
||||
type = types.str;
|
||||
type = lib.types.str;
|
||||
};
|
||||
|
||||
runtimePackages = mkOption {
|
||||
runtimePackages = lib.mkOption {
|
||||
default = [ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ];
|
||||
defaultText = literalExpression "[ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]";
|
||||
defaultText = lib.literalExpression "[ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]";
|
||||
description = lib.mdDoc "Add programs to the buildkite-agent environment";
|
||||
type = types.listOf types.package;
|
||||
type = lib.types.listOf lib.types.package;
|
||||
};
|
||||
|
||||
tokenPath = mkOption {
|
||||
type = types.path;
|
||||
tokenPath = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
description = lib.mdDoc ''
|
||||
The token from your Buildkite "Agents" page.
|
||||
|
||||
|
@ -67,25 +52,25 @@ let
|
|||
'';
|
||||
};
|
||||
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "%hostname-${name}-%n";
|
||||
description = lib.mdDoc ''
|
||||
The name of the agent as seen in the buildkite dashboard.
|
||||
'';
|
||||
};
|
||||
|
||||
tags = mkOption {
|
||||
type = types.attrsOf (types.either types.str (types.listOf types.str));
|
||||
default = {};
|
||||
example = { queue = "default"; docker = "true"; ruby2 ="true"; };
|
||||
tags = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.either lib.types.str (lib.types.listOf lib.types.str));
|
||||
default = { };
|
||||
example = { queue = "default"; docker = "true"; ruby2 = "true"; };
|
||||
description = lib.mdDoc ''
|
||||
Tags for the agent.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
extraConfig = lib.mkOption {
|
||||
type = lib.types.lines;
|
||||
default = "";
|
||||
example = "debug=true";
|
||||
description = lib.mdDoc ''
|
||||
|
@ -93,8 +78,8 @@ let
|
|||
'';
|
||||
};
|
||||
|
||||
privateSshKeyPath = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
privateSshKeyPath = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
default = null;
|
||||
## maximum care is taken so that secrets (ssh keys and the CI token)
|
||||
## don't end up in the Nix store.
|
||||
|
@ -108,67 +93,25 @@ let
|
|||
'';
|
||||
};
|
||||
|
||||
hooks = mkHookOptions [
|
||||
{ name = "checkout";
|
||||
description = ''
|
||||
The `checkout` hook script will replace the default checkout routine of the
|
||||
bootstrap.sh script. You can use this hook to do your own SCM checkout
|
||||
behaviour
|
||||
''; }
|
||||
{ name = "command";
|
||||
description = ''
|
||||
The `command` hook script will replace the default implementation of running
|
||||
the build command.
|
||||
''; }
|
||||
{ name = "environment";
|
||||
description = ''
|
||||
The `environment` hook will run before all other commands, and can be used
|
||||
to set up secrets, data, etc. Anything exported in hooks will be available
|
||||
to the build script.
|
||||
hooks = lib.mkOption {
|
||||
type = lib.types.attrsOf lib.types.lines;
|
||||
default = { };
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
environment = '''
|
||||
export SECRET_VAR=`head -1 /run/keys/secret`
|
||||
''';
|
||||
}'';
|
||||
description = lib.mdDoc ''
|
||||
"Agent" hooks to install.
|
||||
See <https://buildkite.com/docs/agent/v3/hooks> for possible options.
|
||||
'';
|
||||
};
|
||||
|
||||
Note: the contents of this file will be copied to the world-readable
|
||||
Nix store.
|
||||
'';
|
||||
example = ''
|
||||
export SECRET_VAR=`head -1 /run/keys/secret`
|
||||
''; }
|
||||
{ name = "post-artifact";
|
||||
description = ''
|
||||
The `post-artifact` hook will run just after artifacts are uploaded
|
||||
''; }
|
||||
{ name = "post-checkout";
|
||||
description = ''
|
||||
The `post-checkout` hook will run after the bootstrap script has checked out
|
||||
your projects source code.
|
||||
''; }
|
||||
{ name = "post-command";
|
||||
description = ''
|
||||
The `post-command` hook will run after the bootstrap script has run your
|
||||
build commands
|
||||
''; }
|
||||
{ name = "pre-artifact";
|
||||
description = ''
|
||||
The `pre-artifact` hook will run just before artifacts are uploaded
|
||||
''; }
|
||||
{ name = "pre-checkout";
|
||||
description = ''
|
||||
The `pre-checkout` hook will run just before your projects source code is
|
||||
checked out from your SCM provider
|
||||
''; }
|
||||
{ name = "pre-command";
|
||||
description = ''
|
||||
The `pre-command` hook will run just before your build command runs
|
||||
''; }
|
||||
{ name = "pre-exit";
|
||||
description = ''
|
||||
The `pre-exit` hook will run just before your build job finishes
|
||||
''; }
|
||||
];
|
||||
|
||||
hooksPath = mkOption {
|
||||
type = types.path;
|
||||
default = hooksDir config;
|
||||
defaultText = literalMD "generated from {option}`services.buildkite-agents.<name>.hooks`";
|
||||
hooksPath = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
default = hooksDir config.hooks;
|
||||
defaultText = lib.literalMD "generated from {option}`services.buildkite-agents.<name>.hooks`";
|
||||
description = lib.mdDoc ''
|
||||
Path to the directory storing the hooks.
|
||||
Consider using {option}`services.buildkite-agents.<name>.hooks.<name>`
|
||||
|
@ -176,10 +119,10 @@ let
|
|||
'';
|
||||
};
|
||||
|
||||
shell = mkOption {
|
||||
type = types.str;
|
||||
shell = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "${pkgs.bash}/bin/bash -e -c";
|
||||
defaultText = literalExpression ''"''${pkgs.bash}/bin/bash -e -c"'';
|
||||
defaultText = lib.literalExpression ''"''${pkgs.bash}/bin/bash -e -c"'';
|
||||
description = lib.mdDoc ''
|
||||
Command that buildkite-agent 3 will execute when it spawns a shell.
|
||||
'';
|
||||
|
@ -190,9 +133,9 @@ let
|
|||
mapAgents = function: lib.mkMerge (lib.mapAttrsToList function enabledAgents);
|
||||
in
|
||||
{
|
||||
options.services.buildkite-agents = mkOption {
|
||||
type = types.attrsOf (types.submodule buildkiteOptions);
|
||||
default = {};
|
||||
options.services.buildkite-agents = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule buildkiteOptions);
|
||||
default = { };
|
||||
description = lib.mdDoc ''
|
||||
Attribute set of buildkite agents.
|
||||
The attribute key is combined with the hostname and a unique integer to
|
||||
|
@ -213,23 +156,24 @@ in
|
|||
};
|
||||
});
|
||||
config.users.groups = mapAgents (name: cfg: {
|
||||
"buildkite-agent-${name}" = {};
|
||||
"buildkite-agent-${name}" = { };
|
||||
});
|
||||
|
||||
config.systemd.services = mapAgents (name: cfg: {
|
||||
"buildkite-agent-${name}" =
|
||||
{ description = "Buildkite Agent";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
path = cfg.runtimePackages ++ [ cfg.package pkgs.coreutils ];
|
||||
environment = config.networking.proxy.envVars // {
|
||||
HOME = cfg.dataDir;
|
||||
NIX_REMOTE = "daemon";
|
||||
};
|
||||
"buildkite-agent-${name}" = {
|
||||
description = "Buildkite Agent";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
path = cfg.runtimePackages ++ [ cfg.package pkgs.coreutils ];
|
||||
environment = config.networking.proxy.envVars // {
|
||||
HOME = cfg.dataDir;
|
||||
NIX_REMOTE = "daemon";
|
||||
};
|
||||
|
||||
## NB: maximum care is taken so that secrets (ssh keys and the CI token)
|
||||
## don't end up in the Nix store.
|
||||
preStart = let
|
||||
## NB: maximum care is taken so that secrets (ssh keys and the CI token)
|
||||
## don't end up in the Nix store.
|
||||
preStart =
|
||||
let
|
||||
sshDir = "${cfg.dataDir}/.ssh";
|
||||
tagStr = name: value:
|
||||
if lib.isList value
|
||||
|
@ -237,44 +181,39 @@ in
|
|||
else "${name}=${value}";
|
||||
tagsStr = lib.concatStringsSep "," (lib.mapAttrsToList tagStr cfg.tags);
|
||||
in
|
||||
optionalString (cfg.privateSshKeyPath != null) ''
|
||||
mkdir -m 0700 -p "${sshDir}"
|
||||
install -m600 "${toString cfg.privateSshKeyPath}" "${sshDir}/id_rsa"
|
||||
'' + ''
|
||||
cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF
|
||||
token="$(cat ${toString cfg.tokenPath})"
|
||||
name="${cfg.name}"
|
||||
shell="${cfg.shell}"
|
||||
tags="${tagsStr}"
|
||||
build-path="${cfg.dataDir}/builds"
|
||||
hooks-path="${cfg.hooksPath}"
|
||||
${cfg.extraConfig}
|
||||
EOF
|
||||
'';
|
||||
lib.optionalString (cfg.privateSshKeyPath != null) ''
|
||||
mkdir -m 0700 -p "${sshDir}"
|
||||
install -m600 "${toString cfg.privateSshKeyPath}" "${sshDir}/id_rsa"
|
||||
'' + ''
|
||||
cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF
|
||||
token="$(cat ${toString cfg.tokenPath})"
|
||||
name="${cfg.name}"
|
||||
shell="${cfg.shell}"
|
||||
tags="${tagsStr}"
|
||||
build-path="${cfg.dataDir}/builds"
|
||||
hooks-path="${cfg.hooksPath}"
|
||||
${cfg.extraConfig}
|
||||
EOF
|
||||
'';
|
||||
|
||||
serviceConfig =
|
||||
{ ExecStart = "${cfg.package}/bin/buildkite-agent start --config ${cfg.dataDir}/buildkite-agent.cfg";
|
||||
User = "buildkite-agent-${name}";
|
||||
RestartSec = 5;
|
||||
Restart = "on-failure";
|
||||
TimeoutSec = 10;
|
||||
# set a long timeout to give buildkite-agent a chance to finish current builds
|
||||
TimeoutStopSec = "2 min";
|
||||
KillMode = "mixed";
|
||||
};
|
||||
serviceConfig = {
|
||||
ExecStart = "${cfg.package}/bin/buildkite-agent start --config ${cfg.dataDir}/buildkite-agent.cfg";
|
||||
User = "buildkite-agent-${name}";
|
||||
RestartSec = 5;
|
||||
Restart = "on-failure";
|
||||
TimeoutSec = 10;
|
||||
# set a long timeout to give buildkite-agent a chance to finish current builds
|
||||
TimeoutStopSec = "2 min";
|
||||
KillMode = "mixed";
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
config.assertions = mapAgents (name: cfg: [
|
||||
{ assertion = cfg.hooksPath == (hooksDir cfg) || all (v: v == null) (attrValues cfg.hooks);
|
||||
message = ''
|
||||
Options `services.buildkite-agents.${name}.hooksPath' and
|
||||
`services.buildkite-agents.${name}.hooks.<name>' are mutually exclusive.
|
||||
'';
|
||||
}
|
||||
]);
|
||||
|
||||
imports = [
|
||||
(mkRemovedOptionModule [ "services" "buildkite-agent"] "services.buildkite-agent has been upgraded from version 2 to version 3 and moved to an attribute set at services.buildkite-agents. Please consult the 20.03 release notes for more information.")
|
||||
];
|
||||
config.assertions = mapAgents (name: cfg: [{
|
||||
assertion = cfg.hooksPath != hooksDir cfg.hooks -> cfg.hooks == { };
|
||||
message = ''
|
||||
Options `services.buildkite-agents.${name}.hooksPath' and
|
||||
`services.buildkite-agents.${name}.hooks.<name>' are mutually exclusive.
|
||||
'';
|
||||
}]);
|
||||
}
|
||||
|
|
|
@ -210,9 +210,7 @@ in {
|
|||
|
||||
preStart =
|
||||
let replacePlugins =
|
||||
if cfg.plugins == null
|
||||
then ""
|
||||
else
|
||||
optionalString (cfg.plugins != null) (
|
||||
let pluginCmds = lib.attrsets.mapAttrsToList
|
||||
(n: v: "cp ${v} ${cfg.home}/plugins/${n}.jpi")
|
||||
cfg.plugins;
|
||||
|
@ -220,7 +218,7 @@ in {
|
|||
rm -r ${cfg.home}/plugins || true
|
||||
mkdir -p ${cfg.home}/plugins
|
||||
${lib.strings.concatStringsSep "\n" pluginCmds}
|
||||
'';
|
||||
'');
|
||||
in ''
|
||||
rm -rf ${cfg.home}/war
|
||||
${replacePlugins}
|
||||
|
|
632
nixos/modules/services/databases/pgbouncer.nix
Normal file
632
nixos/modules/services/databases/pgbouncer.nix
Normal file
|
@ -0,0 +1,632 @@
|
|||
{ lib, pkgs, config, ... } :
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.pgbouncer;
|
||||
|
||||
confFile = pkgs.writeTextFile {
|
||||
name = "pgbouncer.ini";
|
||||
text = ''
|
||||
[databases]
|
||||
${concatStringsSep "\n"
|
||||
(mapAttrsToList (dbname : settings : "${dbname} = ${settings}") cfg.databases)}
|
||||
|
||||
[users]
|
||||
${concatStringsSep "\n"
|
||||
(mapAttrsToList (username : settings : "${username} = ${settings}") cfg.users)}
|
||||
|
||||
[peers]
|
||||
${concatStringsSep "\n"
|
||||
(mapAttrsToList (peerid : settings : "${peerid} = ${settings}") cfg.peers)}
|
||||
|
||||
[pgbouncer]
|
||||
# general
|
||||
${optionalString (cfg.ignoreStartupParameters != null) "ignore_startup_parameters = ${cfg.ignoreStartupParameters}"}
|
||||
listen_port = ${toString cfg.listenPort}
|
||||
${optionalString (cfg.listenAddress != null) "listen_addr = ${cfg.listenAddress}"}
|
||||
pool_mode = ${cfg.poolMode}
|
||||
max_client_conn = ${toString cfg.maxClientConn}
|
||||
default_pool_size = ${toString cfg.defaultPoolSize}
|
||||
max_user_connections = ${toString cfg.maxUserConnections}
|
||||
max_db_connections = ${toString cfg.maxDbConnections}
|
||||
|
||||
#auth
|
||||
auth_type = ${cfg.authType}
|
||||
${optionalString (cfg.authHbaFile != null) "auth_hba_file = ${cfg.authHbaFile}"}
|
||||
${optionalString (cfg.authFile != null) "auth_file = ${cfg.authFile}"}
|
||||
${optionalString (cfg.authUser != null) "auth_user = ${cfg.authUser}"}
|
||||
${optionalString (cfg.authQuery != null) "auth_query = ${cfg.authQuery}"}
|
||||
${optionalString (cfg.authDbname != null) "auth_dbname = ${cfg.authDbname}"}
|
||||
|
||||
# TLS
|
||||
${optionalString (cfg.tls.client != null) ''
|
||||
client_tls_sslmode = ${cfg.tls.client.sslmode}
|
||||
client_tls_key_file = ${cfg.tls.client.keyFile}
|
||||
client_tls_cert_file = ${cfg.tls.client.certFile}
|
||||
client_tls_ca_file = ${cfg.tls.client.caFile}
|
||||
''}
|
||||
${optionalString (cfg.tls.server != null) ''
|
||||
server_tls_sslmode = ${cfg.tls.server.sslmode}
|
||||
server_tls_key_file = ${cfg.tls.server.keyFile}
|
||||
server_tls_cert_file = ${cfg.tls.server.certFile}
|
||||
server_tls_ca_file = ${cfg.tls.server.caFile}
|
||||
''}
|
||||
|
||||
# log
|
||||
${optionalString (cfg.logFile != null) "logfile = ${cfg.homeDir}/${cfg.logFile}"}
|
||||
${optionalString (cfg.syslog != null) ''
|
||||
syslog = ${if cfg.syslog.enable then "1" else "0"}
|
||||
syslog_ident = ${cfg.syslog.syslogIdent}
|
||||
syslog_facility = ${cfg.syslog.syslogFacility}
|
||||
''}
|
||||
${optionalString (cfg.verbose != null) "verbose = ${toString cfg.verbose}"}
|
||||
|
||||
# console access
|
||||
${optionalString (cfg.adminUsers != null) "admin_users = ${cfg.adminUsers}"}
|
||||
${optionalString (cfg.statsUsers != null) "stats_users = ${cfg.statsUsers}"}
|
||||
|
||||
# linux
|
||||
pidfile = /run/pgbouncer/pgbouncer.pid
|
||||
|
||||
# extra
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
};
|
||||
|
||||
in {
|
||||
|
||||
options.services.pgbouncer = {
|
||||
|
||||
# NixOS settings
|
||||
|
||||
enable = mkEnableOption (lib.mdDoc "PostgreSQL connection pooler");
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.pgbouncer;
|
||||
defaultText = literalExpression "pkgs.pgbouncer";
|
||||
description = lib.mdDoc ''
|
||||
The pgbouncer package to use.
|
||||
'';
|
||||
};
|
||||
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc ''
|
||||
Whether to automatically open the specified TCP port in the firewall.
|
||||
'';
|
||||
};
|
||||
|
||||
# Generic settings
|
||||
|
||||
logFile = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = "pgbouncer.log";
|
||||
description = lib.mdDoc ''
|
||||
Specifies the log file.
|
||||
Either this or syslog has to be specified.
|
||||
'';
|
||||
};
|
||||
|
||||
listenAddress = mkOption {
|
||||
type = types.nullOr types.commas;
|
||||
example = "*";
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
Specifies a list (comma-separated) of addresses where to listen for TCP connections.
|
||||
You may also use * meaning “listen on all addresses”.
|
||||
When not set, only Unix socket connections are accepted.
|
||||
|
||||
Addresses can be specified numerically (IPv4/IPv6) or by name.
|
||||
'';
|
||||
};
|
||||
|
||||
listenPort = mkOption {
|
||||
type = types.port;
|
||||
default = 6432;
|
||||
description = lib.mdDoc ''
|
||||
Which port to listen on. Applies to both TCP and Unix sockets.
|
||||
'';
|
||||
};
|
||||
|
||||
poolMode = mkOption {
|
||||
type = types.enum [ "session" "transaction" "statement" ];
|
||||
default = "session";
|
||||
description = lib.mdDoc ''
|
||||
Specifies when a server connection can be reused by other clients.
|
||||
|
||||
session
|
||||
Server is released back to pool after client disconnects. Default.
|
||||
transaction
|
||||
Server is released back to pool after transaction finishes.
|
||||
statement
|
||||
Server is released back to pool after query finishes.
|
||||
Transactions spanning multiple statements are disallowed in this mode.
|
||||
'';
|
||||
};
|
||||
|
||||
maxClientConn = mkOption {
|
||||
type = types.int;
|
||||
default = 100;
|
||||
description = lib.mdDoc ''
|
||||
Maximum number of client connections allowed.
|
||||
|
||||
When this setting is increased, then the file descriptor limits in the operating system
|
||||
might also have to be increased. Note that the number of file descriptors potentially
|
||||
used is more than maxClientConn. If each user connects under its own user name to the server,
|
||||
the theoretical maximum used is:
|
||||
maxClientConn + (max pool_size * total databases * total users)
|
||||
|
||||
If a database user is specified in the connection string (all users connect under the same user name),
|
||||
the theoretical maximum is:
|
||||
maxClientConn + (max pool_size * total databases)
|
||||
|
||||
The theoretical maximum should never be reached, unless somebody deliberately crafts a special load for it.
|
||||
Still, it means you should set the number of file descriptors to a safely high number.
|
||||
'';
|
||||
};
|
||||
|
||||
defaultPoolSize = mkOption {
|
||||
type = types.int;
|
||||
default = 20;
|
||||
description = lib.mdDoc ''
|
||||
How many server connections to allow per user/database pair.
|
||||
Can be overridden in the per-database configuration.
|
||||
'';
|
||||
};
|
||||
|
||||
maxDbConnections = mkOption {
|
||||
type = types.int;
|
||||
default = 0;
|
||||
description = lib.mdDoc ''
|
||||
Do not allow more than this many server connections per database (regardless of user).
|
||||
This considers the PgBouncer database that the client has connected to,
|
||||
not the PostgreSQL database of the outgoing connection.
|
||||
|
||||
This can also be set per database in the [databases] section.
|
||||
|
||||
Note that when you hit the limit, closing a client connection to one pool will
|
||||
not immediately allow a server connection to be established for another pool,
|
||||
because the server connection for the first pool is still open.
|
||||
Once the server connection closes (due to idle timeout),
|
||||
a new server connection will immediately be opened for the waiting pool.
|
||||
|
||||
0 = unlimited
|
||||
'';
|
||||
};
|
||||
|
||||
maxUserConnections = mkOption {
|
||||
type = types.int;
|
||||
default = 0;
|
||||
description = lib.mdDoc ''
|
||||
Do not allow more than this many server connections per user (regardless of database).
|
||||
This considers the PgBouncer user that is associated with a pool,
|
||||
which is either the user specified for the server connection
|
||||
or in absence of that the user the client has connected as.
|
||||
|
||||
This can also be set per user in the [users] section.
|
||||
|
||||
Note that when you hit the limit, closing a client connection to one pool
|
||||
will not immediately allow a server connection to be established for another pool,
|
||||
because the server connection for the first pool is still open.
|
||||
Once the server connection closes (due to idle timeout), a new server connection
|
||||
will immediately be opened for the waiting pool.
|
||||
|
||||
0 = unlimited
|
||||
'';
|
||||
};
|
||||
|
||||
ignoreStartupParameters = mkOption {
|
||||
type = types.nullOr types.commas;
|
||||
example = "extra_float_digits";
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
By default, PgBouncer allows only parameters it can keep track of in startup packets:
|
||||
client_encoding, datestyle, timezone and standard_conforming_strings.
|
||||
|
||||
All others parameters will raise an error.
|
||||
To allow others parameters, they can be specified here, so that PgBouncer knows that
|
||||
they are handled by the admin and it can ignore them.
|
||||
|
||||
If you need to specify multiple values, use a comma-separated list.
|
||||
|
||||
IMPORTANT: When using prometheus-pgbouncer-exporter, you need:
|
||||
extra_float_digits
|
||||
<https://github.com/prometheus-community/pgbouncer_exporter#pgbouncer-configuration>
|
||||
'';
|
||||
};
|
||||
|
||||
# Section [databases]
|
||||
databases = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = {};
|
||||
example = {
|
||||
exampledb = "host=/run/postgresql/ port=5432 auth_user=exampleuser dbname=exampledb sslmode=require";
|
||||
bardb = "host=localhost dbname=bazdb";
|
||||
foodb = "host=host1.example.com port=5432";
|
||||
};
|
||||
description = lib.mdDoc ''
|
||||
Detailed information about PostgreSQL database definitions:
|
||||
<https://www.pgbouncer.org/config.html#section-databases>
|
||||
'';
|
||||
};
|
||||
|
||||
# Section [users]
|
||||
users = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = {};
|
||||
example = {
|
||||
user1 = "pool_mode=session";
|
||||
};
|
||||
description = lib.mdDoc ''
|
||||
Optional.
|
||||
|
||||
Detailed information about PostgreSQL user definitions:
|
||||
<https://www.pgbouncer.org/config.html#section-users>
|
||||
'';
|
||||
};
|
||||
|
||||
# Section [peers]
|
||||
peers = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = {};
|
||||
example = {
|
||||
"1" = "host=host1.example.com";
|
||||
"2" = "host=/tmp/pgbouncer-2 port=5555";
|
||||
};
|
||||
description = lib.mdDoc ''
|
||||
Optional.
|
||||
|
||||
Detailed information about PostgreSQL database definitions:
|
||||
<https://www.pgbouncer.org/config.html#section-peers>
|
||||
'';
|
||||
};
|
||||
|
||||
# Authentication settings
|
||||
authType = mkOption {
|
||||
type = types.enum [ "cert" "md5" "scram-sha-256" "plain" "trust" "any" "hba" "pam" ];
|
||||
default = "md5";
|
||||
description = lib.mdDoc ''
|
||||
How to authenticate users.
|
||||
|
||||
cert
|
||||
Client must connect over TLS connection with a valid client certificate.
|
||||
The user name is then taken from the CommonName field from the certificate.
|
||||
md5
|
||||
Use MD5-based password check. This is the default authentication method.
|
||||
authFile may contain both MD5-encrypted and plain-text passwords.
|
||||
If md5 is configured and a user has a SCRAM secret, then SCRAM authentication is used automatically instead.
|
||||
scram-sha-256
|
||||
Use password check with SCRAM-SHA-256. authFile has to contain SCRAM secrets or plain-text passwords.
|
||||
plain
|
||||
The clear-text password is sent over the wire. Deprecated.
|
||||
trust
|
||||
No authentication is done. The user name must still exist in authFile.
|
||||
any
|
||||
Like the trust method, but the user name given is ignored.
|
||||
Requires that all databases are configured to log in as a specific user.
|
||||
Additionally, the console database allows any user to log in as admin.
|
||||
hba
|
||||
The actual authentication type is loaded from authHbaFile.
|
||||
This allows different authentication methods for different access paths,
|
||||
for example: connections over Unix socket use the peer auth method, connections over TCP must use TLS.
|
||||
pam
|
||||
PAM is used to authenticate users, authFile is ignored.
|
||||
This method is not compatible with databases using the authUser option.
|
||||
The service name reported to PAM is “pgbouncer”. pam is not supported in the HBA configuration file.
|
||||
'';
|
||||
};
|
||||
|
||||
authHbaFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
example = "/secrets/pgbouncer_hba";
|
||||
description = lib.mdDoc ''
|
||||
HBA configuration file to use when authType is hba.
|
||||
|
||||
See HBA file format details:
|
||||
<https://www.pgbouncer.org/config.html#hba-file-format>
|
||||
'';
|
||||
};
|
||||
|
||||
authFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
example = "/secrets/pgbouncer_authfile";
|
||||
description = lib.mdDoc ''
|
||||
The name of the file to load user names and passwords from.
|
||||
|
||||
See section Authentication file format details:
|
||||
<https://www.pgbouncer.org/config.html#authentication-file-format>
|
||||
|
||||
Most authentication types require that either authFile or authUser be set;
|
||||
otherwise there would be no users defined.
|
||||
'';
|
||||
};
|
||||
|
||||
authUser = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "pgbouncer";
|
||||
description = lib.mdDoc ''
|
||||
If authUser is set, then any user not specified in authFile will be queried
|
||||
through the authQuery query from pg_shadow in the database, using authUser.
|
||||
The password of authUser will be taken from authFile.
|
||||
(If the authUser does not require a password then it does not need to be defined in authFile.)
|
||||
|
||||
Direct access to pg_shadow requires admin rights.
|
||||
It's preferable to use a non-superuser that calls a SECURITY DEFINER function instead.
|
||||
'';
|
||||
};
|
||||
|
||||
authQuery = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "SELECT usename, passwd FROM pg_shadow WHERE usename=$1";
|
||||
description = lib.mdDoc ''
|
||||
Query to load user's password from database.
|
||||
|
||||
Direct access to pg_shadow requires admin rights.
|
||||
It's preferable to use a non-superuser that calls a SECURITY DEFINER function instead.
|
||||
|
||||
Note that the query is run inside the target database.
|
||||
So if a function is used, it needs to be installed into each database.
|
||||
'';
|
||||
};
|
||||
|
||||
authDbname = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "authdb";
|
||||
description = lib.mdDoc ''
|
||||
Database name in the [database] section to be used for authentication purposes.
|
||||
This option can be either global or overriden in the connection string if this parameter is specified.
|
||||
'';
|
||||
};
|
||||
|
||||
# TLS settings
|
||||
tls.client = mkOption {
|
||||
type = types.nullOr (types.submodule {
|
||||
options = {
|
||||
sslmode = mkOption {
|
||||
type = types.enum [ "disable" "allow" "prefer" "require" "verify-ca" "verify-full" ];
|
||||
default = "disable";
|
||||
description = lib.mdDoc ''
|
||||
TLS mode to use for connections from clients.
|
||||
TLS connections are disabled by default.
|
||||
|
||||
When enabled, tls.client.keyFile and tls.client.certFile
|
||||
must be also configured to set up the key and certificate
|
||||
PgBouncer uses to accept client connections.
|
||||
|
||||
disable
|
||||
Plain TCP. If client requests TLS, it's ignored. Default.
|
||||
allow
|
||||
If client requests TLS, it is used. If not, plain TCP is used.
|
||||
If the client presents a client certificate, it is not validated.
|
||||
prefer
|
||||
Same as allow.
|
||||
require
|
||||
Client must use TLS. If not, the client connection is rejected.
|
||||
If the client presents a client certificate, it is not validated.
|
||||
verify-ca
|
||||
Client must use TLS with valid client certificate.
|
||||
verify-full
|
||||
Same as verify-ca
|
||||
'';
|
||||
};
|
||||
certFile = mkOption {
|
||||
type = types.path;
|
||||
example = "/secrets/pgbouncer.key";
|
||||
description = lib.mdDoc "Path to certificate for private key. Clients can validate it";
|
||||
};
|
||||
keyFile = mkOption {
|
||||
type = types.path;
|
||||
example = "/secrets/pgbouncer.crt";
|
||||
description = lib.mdDoc "Path to private key for PgBouncer to accept client connections";
|
||||
};
|
||||
caFile = mkOption {
|
||||
type = types.path;
|
||||
example = "/secrets/pgbouncer.crt";
|
||||
description = lib.mdDoc "Path to root certificate file to validate client certificates";
|
||||
};
|
||||
};
|
||||
});
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
<https://www.pgbouncer.org/config.html#tls-settings>
|
||||
'';
|
||||
};
|
||||
|
||||
tls.server = mkOption {
|
||||
type = types.nullOr (types.submodule {
|
||||
options = {
|
||||
sslmode = mkOption {
|
||||
type = types.enum [ "disable" "allow" "prefer" "require" "verify-ca" "verify-full" ];
|
||||
default = "disable";
|
||||
description = lib.mdDoc ''
|
||||
TLS mode to use for connections to PostgreSQL servers.
|
||||
TLS connections are disabled by default.
|
||||
|
||||
disable
|
||||
Plain TCP. TLS is not even requested from the server. Default.
|
||||
allow
|
||||
FIXME: if server rejects plain, try TLS?
|
||||
prefer
|
||||
TLS connection is always requested first from PostgreSQL.
|
||||
If refused, the connection will be established over plain TCP.
|
||||
Server certificate is not validated.
|
||||
require
|
||||
Connection must go over TLS. If server rejects it, plain TCP is not attempted.
|
||||
Server certificate is not validated.
|
||||
verify-ca
|
||||
Connection must go over TLS and server certificate must be valid according to tls.server.caFile.
|
||||
Server host name is not checked against certificate.
|
||||
verify-full
|
||||
Connection must go over TLS and server certificate must be valid according to tls.server.caFile.
|
||||
Server host name must match certificate information.
|
||||
'';
|
||||
};
|
||||
certFile = mkOption {
|
||||
type = types.path;
|
||||
example = "/secrets/pgbouncer_server.key";
|
||||
description = lib.mdDoc "Certificate for private key. PostgreSQL server can validate it.";
|
||||
};
|
||||
keyFile = mkOption {
|
||||
type = types.path;
|
||||
example = "/secrets/pgbouncer_server.crt";
|
||||
description = lib.mdDoc "Private key for PgBouncer to authenticate against PostgreSQL server.";
|
||||
};
|
||||
caFile = mkOption {
|
||||
type = types.path;
|
||||
example = "/secrets/pgbouncer_server.crt";
|
||||
description = lib.mdDoc "Root certificate file to validate PostgreSQL server certificates.";
|
||||
};
|
||||
};
|
||||
});
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
<https://www.pgbouncer.org/config.html#tls-settings>
|
||||
'';
|
||||
};
|
||||
|
||||
# Log settings
|
||||
syslog = mkOption {
|
||||
type = types.nullOr (types.submodule {
|
||||
options = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc ''
|
||||
Toggles syslog on/off.
|
||||
'';
|
||||
};
|
||||
syslogIdent = mkOption {
|
||||
type = types.str;
|
||||
default = "pgbouncer";
|
||||
description = lib.mdDoc ''
|
||||
Under what name to send logs to syslog.
|
||||
'';
|
||||
};
|
||||
syslogFacility = mkOption {
|
||||
type = types.enum [ "auth" "authpriv" "daemon" "user" "local0" "local1" "local2" "local3" "local4" "local5" "local6" "local7" ];
|
||||
default = "daemon";
|
||||
description = lib.mdDoc ''
|
||||
Under what facility to send logs to syslog.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
<https://www.pgbouncer.org/config.html#log-settings>
|
||||
'';
|
||||
};
|
||||
|
||||
verbose = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 0;
|
||||
description = lib.mdDoc ''
|
||||
Increase verbosity. Mirrors the “-v” switch on the command line.
|
||||
'';
|
||||
};
|
||||
|
||||
# Console access control
|
||||
adminUsers = mkOption {
|
||||
type = types.nullOr types.commas;
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
Comma-separated list of database users that are allowed to connect and run all commands on the console.
|
||||
Ignored when authType is any, in which case any user name is allowed in as admin.
|
||||
'';
|
||||
};
|
||||
|
||||
statsUsers = mkOption {
|
||||
type = types.nullOr types.commas;
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
Comma-separated list of database users that are allowed to connect and run read-only queries on the console.
|
||||
That means all SHOW commands except SHOW FDS.
|
||||
'';
|
||||
};
|
||||
|
||||
# Linux settings
|
||||
openFilesLimit = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 65536;
|
||||
description = lib.mdDoc ''
|
||||
Maximum number of open files.
|
||||
'';
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "pgbouncer";
|
||||
description = lib.mdDoc ''
|
||||
The user pgbouncer is run as.
|
||||
'';
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "pgbouncer";
|
||||
description = lib.mdDoc ''
|
||||
The group pgbouncer is run as.
|
||||
'';
|
||||
};
|
||||
|
||||
homeDir = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/lib/pgbouncer";
|
||||
description = lib.mdDoc ''
|
||||
Specifies the home directory.
|
||||
'';
|
||||
};
|
||||
|
||||
# Extra settings
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
description = lib.mdDoc ''
|
||||
Any additional text to be appended to config.ini
|
||||
<https://www.pgbouncer.org/config.html>.
|
||||
'';
|
||||
default = "";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
users.groups.${cfg.group} = { };
|
||||
users.users.${cfg.user} = {
|
||||
description = "PgBouncer service user";
|
||||
group = cfg.group;
|
||||
home = cfg.homeDir;
|
||||
createHome = true;
|
||||
isSystemUser = true;
|
||||
};
|
||||
|
||||
systemd.services.pgbouncer = {
|
||||
description = "PgBouncer - PostgreSQL connection pooler";
|
||||
wants = [ "postgresql.service" ];
|
||||
after = [ "postgresql.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "forking";
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
ExecStart = "${pkgs.pgbouncer}/bin/pgbouncer -d ${confFile}";
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID";
|
||||
RuntimeDirectory = "pgbouncer";
|
||||
PIDFile = "/run/pgbouncer/pgbouncer.pid";
|
||||
LimitNOFILE = cfg.openFilesLimit;
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
|
||||
|
||||
};
|
||||
|
||||
meta.maintainers = [ maintainers._1000101 ];
|
||||
|
||||
}
|
|
@ -404,8 +404,8 @@ in
|
|||
{
|
||||
log_connections = true;
|
||||
log_statement = "all";
|
||||
logging_collector = true
|
||||
log_disconnections = true
|
||||
logging_collector = true;
|
||||
log_disconnections = true;
|
||||
log_destination = lib.mkForce "syslog";
|
||||
}
|
||||
'';
|
||||
|
|
|
@ -96,7 +96,7 @@ in
|
|||
|
||||
environment.systemPackages = [ cfg.package editorScript desktopApplicationFile ];
|
||||
|
||||
environment.variables.EDITOR = mkIf cfg.defaultEditor (mkOverride 900 "${editorScript}/bin/emacseditor");
|
||||
environment.variables.EDITOR = mkIf cfg.defaultEditor (mkOverride 900 "emacseditor");
|
||||
};
|
||||
|
||||
meta.doc = ./emacs.md;
|
||||
|
|
|
@ -3,12 +3,9 @@ with lib;
|
|||
let
|
||||
cfg = config.services.keyd;
|
||||
settingsFormat = pkgs.formats.ini { };
|
||||
in
|
||||
{
|
||||
options = {
|
||||
services.keyd = {
|
||||
enable = mkEnableOption (lib.mdDoc "keyd, a key remapping daemon");
|
||||
|
||||
keyboardOptions = { ... }: {
|
||||
options = {
|
||||
ids = mkOption {
|
||||
type = types.listOf types.string;
|
||||
default = [ "*" ];
|
||||
|
@ -35,24 +32,71 @@ in
|
|||
};
|
||||
};
|
||||
description = lib.mdDoc ''
|
||||
Configuration, except `ids` section, that is written to {file}`/etc/keyd/default.conf`.
|
||||
Configuration, except `ids` section, that is written to {file}`/etc/keyd/<keyboard>.conf`.
|
||||
Appropriate names can be used to write non-alpha keys, for example "equal" instead of "=" sign (see <https://github.com/NixOS/nixpkgs/issues/236622>).
|
||||
See <https://github.com/rvaiya/keyd> how to configure.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(mkRemovedOptionModule [ "services" "keyd" "ids" ]
|
||||
''Use keyboards.<filename>.ids instead. If you don't need a multi-file configuration, just add keyboards.default before the ids. See https://github.com/NixOS/nixpkgs/pull/243271.'')
|
||||
(mkRemovedOptionModule [ "services" "keyd" "settings" ]
|
||||
''Use keyboards.<filename>.settings instead. If you don't need a multi-file configuration, just add keyboards.default before the settings. See https://github.com/NixOS/nixpkgs/pull/243271.'')
|
||||
];
|
||||
|
||||
options.services.keyd = {
|
||||
enable = mkEnableOption (lib.mdDoc "keyd, a key remapping daemon");
|
||||
|
||||
keyboards = mkOption {
|
||||
type = types.attrsOf (types.submodule keyboardOptions);
|
||||
default = { };
|
||||
example = literalExpression ''
|
||||
{
|
||||
default = {
|
||||
ids = [ "*" ];
|
||||
settings = {
|
||||
main = {
|
||||
capslock = "overload(control, esc)";
|
||||
};
|
||||
};
|
||||
};
|
||||
externalKeyboard = {
|
||||
ids = [ "1ea7:0907" ];
|
||||
settings = {
|
||||
main = {
|
||||
esc = capslock;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
'';
|
||||
description = mdDoc ''
|
||||
Configuration for one or more device IDs. Corresponding files in the /etc/keyd/ directory are created according to the name of the keys (like `default` or `externalKeyboard`).
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
environment.etc."keyd/default.conf".source = pkgs.runCommand "default.conf"
|
||||
{
|
||||
ids = ''
|
||||
[ids]
|
||||
${concatStringsSep "\n" cfg.ids}
|
||||
'';
|
||||
passAsFile = [ "ids" ];
|
||||
} ''
|
||||
cat $idsPath <(echo) ${settingsFormat.generate "keyd-main.conf" cfg.settings} >$out
|
||||
'';
|
||||
# Creates separate files in the `/etc/keyd/` directory for each key in the dictionary
|
||||
environment.etc = mapAttrs'
|
||||
(name: options:
|
||||
nameValuePair "keyd/${name}.conf" {
|
||||
source = pkgs.runCommand "${name}.conf"
|
||||
{
|
||||
ids = ''
|
||||
[ids]
|
||||
${concatStringsSep "\n" options.ids}
|
||||
'';
|
||||
passAsFile = [ "ids" ];
|
||||
} ''
|
||||
cat $idsPath <(echo) ${settingsFormat.generate "keyd-${name}.conf" options.settings} >$out
|
||||
'';
|
||||
})
|
||||
cfg.keyboards;
|
||||
|
||||
hardware.uinput.enable = lib.mkDefault true;
|
||||
|
||||
|
@ -62,9 +106,11 @@ in
|
|||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
restartTriggers = [
|
||||
config.environment.etc."keyd/default.conf".source
|
||||
];
|
||||
restartTriggers = mapAttrsToList
|
||||
(name: options:
|
||||
config.environment.etc."keyd/${name}.conf".source
|
||||
)
|
||||
cfg.keyboards;
|
||||
|
||||
# this is configurable in 2.4.2, later versions seem to remove this option.
|
||||
# post-2.4.2 may need to set makeFlags in the derivation:
|
||||
|
|
270
nixos/modules/services/home-automation/ebusd.nix
Normal file
270
nixos/modules/services/home-automation/ebusd.nix
Normal file
|
@ -0,0 +1,270 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.ebusd;
|
||||
|
||||
package = pkgs.ebusd;
|
||||
|
||||
arguments = [
|
||||
"${package}/bin/ebusd"
|
||||
"--foreground"
|
||||
"--updatecheck=off"
|
||||
"--device=${cfg.device}"
|
||||
"--port=${toString cfg.port}"
|
||||
"--configpath=${cfg.configpath}"
|
||||
"--scanconfig=${cfg.scanconfig}"
|
||||
"--log=main:${cfg.logs.main}"
|
||||
"--log=network:${cfg.logs.network}"
|
||||
"--log=bus:${cfg.logs.bus}"
|
||||
"--log=update:${cfg.logs.update}"
|
||||
"--log=other:${cfg.logs.other}"
|
||||
"--log=all:${cfg.logs.all}"
|
||||
] ++ lib.optionals cfg.readonly [
|
||||
"--readonly"
|
||||
] ++ lib.optionals cfg.mqtt.enable [
|
||||
"--mqtthost=${cfg.mqtt.host}"
|
||||
"--mqttport=${toString cfg.mqtt.port}"
|
||||
"--mqttuser=${cfg.mqtt.user}"
|
||||
"--mqttpass=${cfg.mqtt.password}"
|
||||
] ++ lib.optionals cfg.mqtt.home-assistant [
|
||||
"--mqttint=${package}/etc/ebusd/mqtt-hassio.cfg"
|
||||
"--mqttjson"
|
||||
] ++ lib.optionals cfg.mqtt.retain [
|
||||
"--mqttretain"
|
||||
] ++ cfg.extraArguments;
|
||||
|
||||
usesDev = hasPrefix "/" cfg.device;
|
||||
|
||||
command = concatStringsSep " " arguments;
|
||||
|
||||
in
|
||||
{
|
||||
meta.maintainers = with maintainers; [ nathan-gs ];
|
||||
|
||||
options.services.ebusd = {
|
||||
enable = mkEnableOption (lib.mdDoc "ebusd service");
|
||||
|
||||
device = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
example = "IP:PORT";
|
||||
description = lib.mdDoc ''
|
||||
Use DEV as eBUS device [/dev/ttyUSB0].
|
||||
This can be either:
|
||||
enh:DEVICE or enh:IP:PORT for enhanced device (only adapter v3 and newer),
|
||||
ens:DEVICE for enhanced high speed serial device (only adapter v3 and newer with firmware since 20220731),
|
||||
DEVICE for serial device (normal speed, for all other serial adapters like adapter v2 as well as adapter v3 in non-enhanced mode), or
|
||||
[udp:]IP:PORT for network device.
|
||||
https://github.com/john30/ebusd/wiki/2.-Run#device-options
|
||||
'';
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
default = 8888;
|
||||
type = types.port;
|
||||
description = lib.mdDoc ''
|
||||
The port on which to listen on
|
||||
'';
|
||||
};
|
||||
|
||||
readonly = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc ''
|
||||
Only read from device, never write to it
|
||||
'';
|
||||
};
|
||||
|
||||
configpath = mkOption {
|
||||
type = types.str;
|
||||
default = "https://cfg.ebusd.eu/";
|
||||
description = lib.mdDoc ''
|
||||
Read CSV config files from PATH (local folder or HTTPS URL) [https://cfg.ebusd.eu/]
|
||||
'';
|
||||
};
|
||||
|
||||
scanconfig = mkOption {
|
||||
type = types.str;
|
||||
default = "full";
|
||||
description = lib.mdDoc ''
|
||||
Pick CSV config files matching initial scan ("none" or empty for no initial scan message, "full" for full scan, or a single hex address to scan, default is to send a broadcast ident message).
|
||||
If combined with --checkconfig, you can add scan message data as arguments for checking a particular scan configuration, e.g. "FF08070400/0AB5454850303003277201". For further details on this option,
|
||||
see [Automatic configuration](https://github.com/john30/ebusd/wiki/4.7.-Automatic-configuration).
|
||||
'';
|
||||
};
|
||||
|
||||
logs = {
|
||||
main = mkOption {
|
||||
type = types.enum [ "error" "notice" "info" "debug"];
|
||||
default = "info";
|
||||
description = lib.mdDoc ''
|
||||
Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice].
|
||||
'';
|
||||
};
|
||||
|
||||
network = mkOption {
|
||||
type = types.enum [ "error" "notice" "info" "debug"];
|
||||
default = "info";
|
||||
description = lib.mdDoc ''
|
||||
Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice].
|
||||
'';
|
||||
};
|
||||
|
||||
bus = mkOption {
|
||||
type = types.enum [ "error" "notice" "info" "debug"];
|
||||
default = "info";
|
||||
description = lib.mdDoc ''
|
||||
Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice].
|
||||
'';
|
||||
};
|
||||
|
||||
update = mkOption {
|
||||
type = types.enum [ "error" "notice" "info" "debug"];
|
||||
default = "info";
|
||||
description = lib.mdDoc ''
|
||||
Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice].
|
||||
'';
|
||||
};
|
||||
|
||||
other = mkOption {
|
||||
type = types.enum [ "error" "notice" "info" "debug"];
|
||||
default = "info";
|
||||
description = lib.mdDoc ''
|
||||
Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice].
|
||||
'';
|
||||
};
|
||||
|
||||
all = mkOption {
|
||||
type = types.enum [ "error" "notice" "info" "debug"];
|
||||
default = "info";
|
||||
description = lib.mdDoc ''
|
||||
Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice].
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
mqtt = {
|
||||
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc ''
|
||||
Adds support for MQTT
|
||||
'';
|
||||
};
|
||||
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = "localhost";
|
||||
description = lib.mdDoc ''
|
||||
Connect to MQTT broker on HOST.
|
||||
'';
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
default = 1883;
|
||||
type = types.port;
|
||||
description = lib.mdDoc ''
|
||||
The port on which to connect to MQTT
|
||||
'';
|
||||
};
|
||||
|
||||
home-assistant = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc ''
|
||||
Adds the Home Assistant topics to MQTT, read more at [MQTT Integration](https://github.com/john30/ebusd/wiki/MQTT-integration)
|
||||
'';
|
||||
};
|
||||
|
||||
retain = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc ''
|
||||
Set the retain flag on all topics instead of only selected global ones
|
||||
'';
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
description = lib.mdDoc ''
|
||||
The MQTT user to use
|
||||
'';
|
||||
};
|
||||
|
||||
password = mkOption {
|
||||
type = types.str;
|
||||
description = lib.mdDoc ''
|
||||
The MQTT password.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
extraArguments = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = lib.mdDoc ''
|
||||
Extra arguments to the ebus daemon
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = mkIf (cfg.enable) {
|
||||
|
||||
systemd.services.ebusd = {
|
||||
description = "EBUSd Service";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
ExecStart = command;
|
||||
DynamicUser = true;
|
||||
Restart = "on-failure";
|
||||
|
||||
# Hardening
|
||||
CapabilityBoundingSet = "";
|
||||
DeviceAllow = lib.optionals usesDev [
|
||||
cfg.device
|
||||
] ;
|
||||
DevicePolicy = "closed";
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = false;
|
||||
NoNewPrivileges = true;
|
||||
PrivateDevices = usesDev;
|
||||
PrivateUsers = true;
|
||||
PrivateTmp = true;
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectProc = "invisible";
|
||||
ProcSubset = "pid";
|
||||
ProtectSystem = "strict";
|
||||
RemoveIPC = true;
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SupplementaryGroups = [
|
||||
"dialout"
|
||||
];
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [
|
||||
"@system-service @pkey"
|
||||
"~@privileged @resources"
|
||||
];
|
||||
UMask = "0077";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
}
|
|
@ -50,7 +50,7 @@ in
|
|||
];
|
||||
environment.HOME = "/var/lib/evcc";
|
||||
path = with pkgs; [
|
||||
glibc # requires getent
|
||||
getent
|
||||
];
|
||||
serviceConfig = {
|
||||
ExecStart = "${package}/bin/evcc --config ${configFile} ${escapeShellArgs cfg.extraArgs}";
|
||||
|
|
|
@ -203,7 +203,7 @@ with lib;
|
|||
users = {
|
||||
users.${cfg.user} = {
|
||||
description = "Nullmailer relay-only mta user";
|
||||
group = cfg.group;
|
||||
inherit (cfg) group;
|
||||
isSystemUser = true;
|
||||
};
|
||||
|
||||
|
@ -211,10 +211,10 @@ with lib;
|
|||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /var/spool/nullmailer - ${cfg.user} - - -"
|
||||
"d /var/spool/nullmailer/failed 750 ${cfg.user} - - -"
|
||||
"d /var/spool/nullmailer/queue 750 ${cfg.user} - - -"
|
||||
"d /var/spool/nullmailer/tmp 750 ${cfg.user} - - -"
|
||||
"d /var/spool/nullmailer - ${cfg.user} ${cfg.group} - -"
|
||||
"d /var/spool/nullmailer/failed 770 ${cfg.user} ${cfg.group} - -"
|
||||
"d /var/spool/nullmailer/queue 770 ${cfg.user} ${cfg.group} - -"
|
||||
"d /var/spool/nullmailer/tmp 770 ${cfg.user} ${cfg.group} - -"
|
||||
];
|
||||
|
||||
systemd.services.nullmailer = {
|
||||
|
@ -238,7 +238,7 @@ with lib;
|
|||
program = "sendmail";
|
||||
source = "${pkgs.nullmailer}/bin/sendmail";
|
||||
owner = cfg.user;
|
||||
group = cfg.group;
|
||||
inherit (cfg) group;
|
||||
setuid = true;
|
||||
setgid = true;
|
||||
};
|
||||
|
|
|
@ -187,7 +187,7 @@ in {
|
|||
sed -i "s/^as_token:.*$/$as_token/g" ${registrationFile}
|
||||
fi
|
||||
# Allow synapse access to the registration
|
||||
if ${getBin pkgs.glibc}/bin/getent group matrix-synapse > /dev/null; then
|
||||
if ${pkgs.getent}/bin/getent group matrix-synapse > /dev/null; then
|
||||
chgrp matrix-synapse ${registrationFile}
|
||||
chmod g+r ${registrationFile}
|
||||
fi
|
||||
|
|
|
@ -138,10 +138,12 @@ in
|
|||
"~@privileged"
|
||||
];
|
||||
StateDirectory = "matrix-conduit";
|
||||
StateDirectoryMode = "0700";
|
||||
ExecStart = "${cfg.package}/bin/conduit";
|
||||
Restart = "on-failure";
|
||||
RestartSec = 10;
|
||||
StartLimitBurst = 5;
|
||||
UMask = "077";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
96
nixos/modules/services/matrix/matrix-sliding-sync.nix
Normal file
96
nixos/modules/services/matrix/matrix-sliding-sync.nix
Normal file
|
@ -0,0 +1,96 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.services.matrix-synapse.sliding-sync;
|
||||
in
|
||||
{
|
||||
options.services.matrix-synapse.sliding-sync = {
|
||||
enable = lib.mkEnableOption (lib.mdDoc "sliding sync");
|
||||
|
||||
package = lib.mkPackageOption pkgs "matrix-sliding-sync" { };
|
||||
|
||||
settings = lib.mkOption {
|
||||
type = lib.types.submodule {
|
||||
freeformType = with lib.types; attrsOf str;
|
||||
options = {
|
||||
SYNCV3_SERVER = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc ''
|
||||
The destination homeserver to talk to not including `/_matrix/` e.g `https://matrix.example.org`.
|
||||
'';
|
||||
};
|
||||
|
||||
SYNCV3_DB = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "postgresql:///matrix-sliding-sync?host=/run/postgresql";
|
||||
description = lib.mdDoc ''
|
||||
The postgres connection string.
|
||||
Refer to <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING>.
|
||||
'';
|
||||
};
|
||||
|
||||
SYNCV3_BINDADDR = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "127.0.0.1:8009";
|
||||
example = "[::]:8008";
|
||||
description = lib.mdDoc "The interface and port to listen on.";
|
||||
};
|
||||
|
||||
SYNCV3_LOG_LEVEL = lib.mkOption {
|
||||
type = lib.types.enum [ "trace" "debug" "info" "warn" "error" "fatal" ];
|
||||
default = "info";
|
||||
description = lib.mdDoc "The level of verbosity for messages logged.";
|
||||
};
|
||||
};
|
||||
};
|
||||
default = { };
|
||||
description = ''
|
||||
Freeform environment variables passed to the sliding sync proxy.
|
||||
Refer to <https://github.com/matrix-org/sliding-sync#setup> for all supported values.
|
||||
'';
|
||||
};
|
||||
|
||||
createDatabase = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = lib.mdDoc ''
|
||||
Whether to enable and configure `services.postgres` to ensure that the database user `matrix-sliding-sync`
|
||||
and the database `matrix-sliding-sync` exist.
|
||||
'';
|
||||
};
|
||||
|
||||
environmentFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = lib.mdDoc ''
|
||||
Environment file as defined in {manpage}`systemd.exec(5)`.
|
||||
|
||||
This must contain the {env}`SYNCV3_SECRET` variable which should
|
||||
be generated with {command}`openssl rand -hex 32`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.postgresql = lib.optionalAttrs cfg.createDatabase {
|
||||
enable = true;
|
||||
ensureDatabases = [ "matrix-sliding-sync" ];
|
||||
ensureUsers = [ rec {
|
||||
name = "matrix-sliding-sync";
|
||||
ensurePermissions."DATABASE \"${name}\"" = "ALL PRIVILEGES";
|
||||
} ];
|
||||
};
|
||||
|
||||
systemd.services.matrix-sliding-sync = {
|
||||
after = lib.optional cfg.createDatabase "postgresql.service";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment = cfg.settings;
|
||||
serviceConfig = {
|
||||
DynamicUser = true;
|
||||
EnvironmentFile = cfg.environmentFile;
|
||||
ExecStart = lib.getExe cfg.package;
|
||||
StateDirectory = "matrix-sliding-sync";
|
||||
WorkingDirectory = "%S/matrix-sliding-sync";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -5,7 +5,9 @@ with lib;
|
|||
let
|
||||
cfg = config.services.ananicy;
|
||||
configFile = pkgs.writeText "ananicy.conf" (generators.toKeyValue { } cfg.settings);
|
||||
extraRules = pkgs.writeText "extraRules" cfg.extraRules;
|
||||
extraRules = pkgs.writeText "extraRules" (concatMapStringsSep "\n" (l: builtins.toJSON l) cfg.extraRules);
|
||||
extraTypes = pkgs.writeText "extraTypes" (concatMapStringsSep "\n" (l: builtins.toJSON l) cfg.extraTypes);
|
||||
extraCgroups = pkgs.writeText "extraCgroups" (concatMapStringsSep "\n" (l: builtins.toJSON l) cfg.extraCgroups);
|
||||
servicename = if ((lib.getName cfg.package) == (lib.getName pkgs.ananicy-cpp)) then "ananicy-cpp" else "ananicy";
|
||||
in
|
||||
{
|
||||
|
@ -23,6 +25,16 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
rulesProvider = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.ananicy;
|
||||
defaultText = literalExpression "pkgs.ananicy";
|
||||
example = literalExpression "pkgs.ananicy-cpp";
|
||||
description = lib.mdDoc ''
|
||||
Which package to copy default rules,types,cgroups from.
|
||||
'';
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
type = with types; attrsOf (oneOf [ int bool str ]);
|
||||
default = { };
|
||||
|
@ -35,20 +47,40 @@ in
|
|||
};
|
||||
|
||||
extraRules = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
type = with types; listOf attrs;
|
||||
default = [ ];
|
||||
description = lib.mdDoc ''
|
||||
Extra rules in json format on separate lines. See:
|
||||
Rules to write in 'nixRules.rules'. See:
|
||||
<https://github.com/Nefelim4ag/Ananicy#configuration>
|
||||
<https://gitlab.com/ananicy-cpp/ananicy-cpp/#global-configuration>
|
||||
'';
|
||||
example = literalExpression ''
|
||||
'''
|
||||
{ "name": "eog", "type": "Image-View" }
|
||||
{ "name": "fdupes", "type": "BG_CPUIO" }
|
||||
'''
|
||||
example = [
|
||||
{ name = "eog"; type = "Image-Viewer"; }
|
||||
{ name = "fdupes"; type = "BG_CPUIO"; }
|
||||
];
|
||||
};
|
||||
extraTypes = mkOption {
|
||||
type = with types; listOf attrs;
|
||||
default = [ ];
|
||||
description = lib.mdDoc ''
|
||||
Types to write in 'nixTypes.types'. See:
|
||||
<https://gitlab.com/ananicy-cpp/ananicy-cpp/#types>
|
||||
'';
|
||||
|
||||
example = [
|
||||
{ type = "my_type"; nice = 19; other_parameter = "value"; }
|
||||
{ type = "compiler"; nice = 19; sched = "batch"; ioclass = "idle"; }
|
||||
];
|
||||
};
|
||||
extraCgroups = mkOption {
|
||||
type = with types; listOf attrs;
|
||||
default = [ ];
|
||||
description = lib.mdDoc ''
|
||||
Cgroups to write in 'nixCgroups.cgroups'. See:
|
||||
<https://gitlab.com/ananicy-cpp/ananicy-cpp/#cgroups>
|
||||
'';
|
||||
example = [
|
||||
{ cgroup = "cpu80"; CPUQuota = 80; }
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -59,10 +91,18 @@ in
|
|||
etc."ananicy.d".source = pkgs.runCommandLocal "ananicyfiles" { } ''
|
||||
mkdir -p $out
|
||||
# ananicy-cpp does not include rules or settings on purpose
|
||||
cp -r ${pkgs.ananicy}/etc/ananicy.d/* $out
|
||||
rm $out/ananicy.conf
|
||||
if [[ -d "${cfg.rulesProvider}/etc/ananicy.d/00-default" ]]; then
|
||||
cp -r ${cfg.rulesProvider}/etc/ananicy.d/* $out
|
||||
else
|
||||
cp -r ${cfg.rulesProvider}/* $out
|
||||
fi
|
||||
|
||||
# configured through .setings
|
||||
rm -f $out/ananicy.conf
|
||||
cp ${configFile} $out/ananicy.conf
|
||||
${optionalString (cfg.extraRules != "") "cp ${extraRules} $out/nixRules.rules"}
|
||||
${optionalString (cfg.extraRules != [ ]) "cp ${extraRules} $out/nixRules.rules"}
|
||||
${optionalString (cfg.extraTypes != [ ]) "cp ${extraTypes} $out/nixTypes.types"}
|
||||
${optionalString (cfg.extraCgroups != [ ]) "cp ${extraCgroups} $out/nixCgroups.cgroups"}
|
||||
'';
|
||||
};
|
||||
|
||||
|
@ -85,6 +125,7 @@ in
|
|||
# https://gitlab.com/ananicy-cpp/ananicy-cpp/-/blob/master/src/config.cpp#L12
|
||||
loglevel = mkOD "warn"; # default is info but its spammy
|
||||
cgroup_realtime_workaround = mkOD config.systemd.enableUnifiedCgroupHierarchy;
|
||||
log_applied_rule = mkOD false;
|
||||
} else {
|
||||
# https://github.com/Nefelim4ag/Ananicy/blob/master/ananicy.d/ananicy.conf
|
||||
check_disks_schedulers = mkOD true;
|
||||
|
|
175
nixos/modules/services/misc/bcg.nix
Normal file
175
nixos/modules/services/misc/bcg.nix
Normal file
|
@ -0,0 +1,175 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.bcg;
|
||||
configFile = (pkgs.formats.yaml {}).generate "bcg.conf.yaml" (
|
||||
filterAttrsRecursive (n: v: v != null) {
|
||||
inherit (cfg) device name mqtt;
|
||||
retain_node_messages = cfg.retainNodeMessages;
|
||||
qos_node_messages = cfg.qosNodeMessages;
|
||||
base_topic_prefix = cfg.baseTopicPrefix;
|
||||
automatic_remove_kit_from_names = cfg.automaticRemoveKitFromNames;
|
||||
automatic_rename_kit_nodes = cfg.automaticRenameKitNodes;
|
||||
automatic_rename_generic_nodes = cfg.automaticRenameGenericNodes;
|
||||
automatic_rename_nodes = cfg.automaticRenameNodes;
|
||||
}
|
||||
);
|
||||
in
|
||||
{
|
||||
options = {
|
||||
services.bcg = {
|
||||
enable = mkEnableOption (mdDoc "BigClown gateway");
|
||||
package = mkOption {
|
||||
default = pkgs.python3Packages.bcg;
|
||||
defaultText = literalExpression "pkgs.python3Packages.bcg";
|
||||
description = mdDoc "Which bcg derivation to use.";
|
||||
type = types.package;
|
||||
};
|
||||
environmentFiles = mkOption {
|
||||
type = types.listOf types.path;
|
||||
default = [];
|
||||
example = [ "/run/keys/bcg.env" ];
|
||||
description = mdDoc ''
|
||||
File to load as environment file. Environment variables from this file
|
||||
will be interpolated into the config file using envsubst with this
|
||||
syntax: `$ENVIRONMENT` or `''${VARIABLE}`.
|
||||
This is useful to avoid putting secrets into the nix store.
|
||||
'';
|
||||
};
|
||||
verbose = mkOption {
|
||||
type = types.enum ["CRITICAL" "ERROR" "WARNING" "INFO" "DEBUG"];
|
||||
default = "WARNING";
|
||||
description = mdDoc "Verbosity level.";
|
||||
};
|
||||
device = mkOption {
|
||||
type = types.str;
|
||||
description = mdDoc "Device name to configure gateway to use.";
|
||||
};
|
||||
name = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = mdDoc ''
|
||||
Name for the device.
|
||||
|
||||
Supported variables:
|
||||
* `{ip}` IP address
|
||||
* `{id}` The ID of the connected usb-dongle or core-module
|
||||
|
||||
`null` can be used for automatic detection from gateway firmware.
|
||||
'';
|
||||
};
|
||||
mqtt = {
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1";
|
||||
description = mdDoc "Host where MQTT server is running.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 1883;
|
||||
description = mdDoc "Port of MQTT server.";
|
||||
};
|
||||
username = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = mdDoc "MQTT server access username.";
|
||||
};
|
||||
password = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = mdDoc "MQTT server access password.";
|
||||
};
|
||||
cafile = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = mdDoc "Certificate Authority file for MQTT server access.";
|
||||
};
|
||||
certfile = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = mdDoc "Certificate file for MQTT server access.";
|
||||
};
|
||||
keyfile = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = mdDoc "Key file for MQTT server access.";
|
||||
};
|
||||
};
|
||||
retainNodeMessages = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = mdDoc "Specify that node messages should be retaied in MQTT broker.";
|
||||
};
|
||||
qosNodeMessages = mkOption {
|
||||
type = types.int;
|
||||
default = 1;
|
||||
description = mdDoc "Set the guarantee of MQTT message delivery.";
|
||||
};
|
||||
baseTopicPrefix = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = mdDoc "Topic prefix added to all MQTT messages.";
|
||||
};
|
||||
automaticRemoveKitFromNames = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = mdDoc "Automatically remove kits.";
|
||||
};
|
||||
automaticRenameKitNodes = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = mdDoc "Automatically rename kit's nodes.";
|
||||
};
|
||||
automaticRenameGenericNodes = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = mdDoc "Automatically rename generic nodes.";
|
||||
};
|
||||
automaticRenameNodes = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = mdDoc "Automatically rename all nodes.";
|
||||
};
|
||||
rename = mkOption {
|
||||
type = with types; attrsOf str;
|
||||
default = {};
|
||||
description = mdDoc "Rename nodes to different name.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
environment.systemPackages = with pkgs; [
|
||||
python3Packages.bcg
|
||||
python3Packages.bch
|
||||
];
|
||||
|
||||
systemd.services.bcg = let
|
||||
envConfig = cfg.environmentFiles != [];
|
||||
finalConfig = if envConfig
|
||||
then "$RUNTIME_DIRECTORY/bcg.config.yaml"
|
||||
else configFile;
|
||||
in {
|
||||
description = "BigClown Gateway";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
wants = mkIf config.services.mosquitto.enable [ "mosquitto.service" ];
|
||||
after = [ "network-online.target" ];
|
||||
preStart = ''
|
||||
umask 077
|
||||
${pkgs.envsubst}/bin/envsubst -i "${configFile}" -o "${finalConfig}"
|
||||
'';
|
||||
serviceConfig = {
|
||||
EnvironmentFile = cfg.environmentFiles;
|
||||
ExecStart="${cfg.package}/bin/bcg -c ${finalConfig} -v ${cfg.verbose}";
|
||||
RuntimeDirectory = "bcg";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue