Merge branch 'master' of github.com:xosnrdev/nixpkgs into add-cargonode-0.1.2

pull from master branch
This commit is contained in:
xosnrdev
2024-12-07 07:05:43 -05:00
1034 changed files with 18989 additions and 27928 deletions
+39
View File
@@ -0,0 +1,39 @@
---
name: Tracking issue
about: Provide an overview on a multi-step effort
title: 'Tracking issue: ISSUENAME'
labels: '5.scope: tracking'
assignees: ''
---
### Tracking description
<!--
Provide a brief summary of the project or multi-step effort. Explain why it is
necessary and what the goals are.
You may include way to reproduce the problem, screenshots or any information
that you find relevant.
-->
#### Reference Issue(s)/PR(s)
- ...
### Follow-up issues/notes
<!--
List any follow-up issues that need to be addressed after the main tasks are
completed, or any notes related to the project.
-->
#### Additional context
Add any other context about the problem here.
---
Add a :+1: [reaction] to [issues you find important].
[reaction]: https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/
[issues you find important]: https://github.com/NixOS/nixpkgs/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc
+1 -1
View File
@@ -83,7 +83,7 @@ jobs:
if (( "${#unformattedFiles[@]}" > 0 )); then
echo "Some new/changed Nix files are not properly formatted"
echo "Please go to the Nixpkgs root directory, run \`nix-shell\`, then:"
echo "Please format them using the Nixpkgs-specific \`nixfmt\` by going to the Nixpkgs root directory, running \`nix-shell\`, then:"
echo "nixfmt ${unformattedFiles[*]@Q}"
echo "Make sure your branch is up to date with master, rebase if not."
echo "If you're having trouble, please ping @NixOS/nix-formatting"
+30
View File
@@ -0,0 +1,30 @@
name: "Building Nixpkgs lib-tests"
permissions:
contents: read
on:
pull_request_target:
paths:
- 'lib/**'
jobs:
get-merge-commit:
uses: ./.github/workflows/get-merge-commit.yml
nixpkgs-lib-tests:
name: nixpkgs-lib-tests
runs-on: ubuntu-latest
needs: get-merge-commit
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
if: needs.get-merge-commit.outputs.mergedSha
with:
# pull_request_target checks out the base branch by default
ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
- uses: cachix/install-nix-action@08dcb3a5e62fa31e2da3d490afc4176ef55ecd72 # v30
with:
# explicitly enable sandbox
extra_nix_config: sandbox = true
- name: Building Nixpkgs lib-tests
run: |
nix-build --arg pkgs "(import ./ci/. {}).pkgs" ./lib/tests/release.nix
+32 -35
View File
@@ -16,53 +16,29 @@ permissions:
contents: read
jobs:
get-merge-commit:
uses: ./.github/workflows/get-merge-commit.yml
attrs:
name: Attributes
runs-on: ubuntu-latest
needs: get-merge-commit
outputs:
mergedSha: ${{ steps.merged.outputs.mergedSha }}
mergedSha: ${{ needs.get-merge-commit.outputs.mergedSha }}
baseSha: ${{ steps.baseSha.outputs.baseSha }}
systems: ${{ steps.systems.outputs.systems }}
steps:
# Important: Because of `pull_request_target`, this doesn't check out the PR,
# but rather the base branch of the PR, which is needed so we don't run untrusted code
- name: Check out the ci directory of the base branch
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: base
sparse-checkout: ci
- name: Check if the PR can be merged and get the test merge commit
id: merged
env:
GH_TOKEN: ${{ github.token }}
GH_EVENT: ${{ github.event_name }}
run: |
case "$GH_EVENT" in
push)
echo "mergedSha=${{ github.sha }}" >> "$GITHUB_OUTPUT"
;;
pull_request_target)
if mergedSha=$(base/ci/get-merge-commit.sh ${{ github.repository }} ${{ github.event.number }}); then
echo "Checking the merge commit $mergedSha"
echo "mergedSha=$mergedSha" >> "$GITHUB_OUTPUT"
else
# Skipping so that no notifications are sent
echo "Skipping the rest..."
fi
;;
esac
rm -rf base
- name: Check out the PR at the test merge commit
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# Add this to _all_ subsequent steps to skip them
if: steps.merged.outputs.mergedSha
if: needs.get-merge-commit.outputs.mergedSha
with:
ref: ${{ steps.merged.outputs.mergedSha }}
ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
fetch-depth: 2
path: nixpkgs
- name: Determine base commit
if: github.event_name == 'pull_request_target' && steps.merged.outputs.mergedSha
if: github.event_name == 'pull_request_target' && needs.get-merge-commit.outputs.mergedSha
id: baseSha
run: |
baseSha=$(git -C nixpkgs rev-parse HEAD^1)
@@ -70,18 +46,18 @@ jobs:
- name: Install Nix
uses: cachix/install-nix-action@08dcb3a5e62fa31e2da3d490afc4176ef55ecd72 # v30
if: steps.merged.outputs.mergedSha
if: needs.get-merge-commit.outputs.mergedSha
- name: Evaluate the list of all attributes and get the systems matrix
id: systems
if: steps.merged.outputs.mergedSha
if: needs.get-merge-commit.outputs.mergedSha
run: |
nix-build nixpkgs/ci -A eval.attrpathsSuperset
echo "systems=$(<result/systems.json)" >> "$GITHUB_OUTPUT"
- name: Upload the list of all attributes
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
if: steps.merged.outputs.mergedSha
if: needs.get-merge-commit.outputs.mergedSha
with:
name: paths
path: result/*
@@ -246,6 +222,7 @@ jobs:
if: needs.process.outputs.baseRunId
permissions:
pull-requests: write
statuses: write
steps:
- name: Download process result
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
@@ -285,3 +262,23 @@ jobs:
GH_TOKEN: ${{ github.token }}
REPOSITORY: ${{ github.repository }}
NUMBER: ${{ github.event.number }}
- name: Add eval summary to commit statuses
if: ${{ github.event_name == 'pull_request_target' }}
run: |
description=$(jq -r '
"Package: added " + (.attrdiff.added | length | tostring) +
", removed " + (.attrdiff.removed | length | tostring) +
", changed " + (.attrdiff.changed | length | tostring) +
", Rebuild: linux " + (.rebuildCountByKernel.linux | tostring) +
", darwin " + (.rebuildCountByKernel.darwin | tostring)
' <comparison/changed-paths.json)
target_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID?pr=$NUMBER"
gh api --method POST \
-H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/$GITHUB_REPOSITORY/statuses/$PR_HEAD_SHA" \
-f "context=Eval / Summary" -f "state=success" -f "description=$description" -f "target_url=$target_url"
env:
GH_TOKEN: ${{ github.token }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
NUMBER: ${{ github.event.number }}
+43
View File
@@ -0,0 +1,43 @@
name: Get merge commit
on:
workflow_call:
outputs:
mergedSha:
description: "The merge commit SHA"
value: ${{ jobs.resolve-merge-commit.outputs.mergedSha }}
# We need a token to query the API, but it doesn't need any special permissions
permissions: {}
jobs:
resolve-merge-commit:
runs-on: ubuntu-latest
outputs:
mergedSha: ${{ steps.merged.outputs.mergedSha }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: base
sparse-checkout: ci
- name: Check if the PR can be merged and get the test merge commit
id: merged
env:
GH_TOKEN: ${{ github.token }}
GH_EVENT: ${{ github.event_name }}
run: |
case "$GH_EVENT" in
push)
echo "mergedSha=${{ github.sha }}" >> "$GITHUB_OUTPUT"
;;
pull_request_target)
if mergedSha=$(base/ci/get-merge-commit.sh ${{ github.repository }} ${{ github.event.number }}); then
echo "Checking the merge commit $mergedSha"
echo "mergedSha=$mergedSha" >> "$GITHUB_OUTPUT"
else
# Skipping so that no notifications are sent
echo "Skipping the rest..."
fi
;;
esac
rm -rf base
+10 -22
View File
@@ -19,46 +19,34 @@ permissions: {}
# There is a feature request for suppressing notifications on concurrency-canceled runs: https://github.com/orgs/community/discussions/13015
jobs:
get-merge-commit:
uses: ./.github/workflows/get-merge-commit.yml
check:
name: nixpkgs-vet
# This needs to be x86_64-linux, because we depend on the tooling being pre-built in the GitHub releases.
runs-on: ubuntu-latest
# This should take 1 minute at most, but let's be generous. The default of 6 hours is definitely too long.
timeout-minutes: 10
needs: get-merge-commit
steps:
# This checks out the base branch because of pull_request_target
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: base
sparse-checkout: ci
- name: Resolving the merge commit
env:
GH_TOKEN: ${{ github.token }}
run: |
if mergedSha=$(base/ci/get-merge-commit.sh ${{ github.repository }} ${{ github.event.number }}); then
echo "Checking the merge commit $mergedSha"
echo "mergedSha=$mergedSha" >> "$GITHUB_ENV"
else
echo "Skipping the rest..."
fi
rm -rf base
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
if: env.mergedSha
if: needs.get-merge-commit.outputs.mergedSha
with:
# pull_request_target checks out the base branch by default
ref: ${{ env.mergedSha }}
ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
# Fetches the merge commit and its parents
fetch-depth: 2
- name: Checking out base branch
if: env.mergedSha
if: needs.get-merge-commit.outputs.mergedSha
run: |
base=$(mktemp -d)
git worktree add "$base" "$(git rev-parse HEAD^1)"
echo "base=$base" >> "$GITHUB_ENV"
- uses: cachix/install-nix-action@08dcb3a5e62fa31e2da3d490afc4176ef55ecd72 # v30
if: env.mergedSha
if: needs.get-merge-commit.outputs.mergedSha
- name: Fetching the pinned tool
if: env.mergedSha
if: needs.get-merge-commit.outputs.mergedSha
# Update the pinned version using ci/nixpkgs-vet/update-pinned-tool.sh
run: |
# The pinned version of the tooling to use.
@@ -71,7 +59,7 @@ jobs:
# Adds a result symlink as a GC root.
nix-store --realise "$toolPath" --add-root result
- name: Running nixpkgs-vet
if: env.mergedSha
if: needs.get-merge-commit.outputs.mergedSha
env:
# Force terminal colors to be enabled. The library that `nixpkgs-vet` uses respects https://bixense.com/clicolors/
CLICOLOR_FORCE: 1
+22
View File
@@ -0,0 +1,22 @@
queue_rules:
# This rule is for https://docs.mergify.com/commands/queue/
# and can be triggered with: @mergifyio queue
- name: default
merge_conditions:
# all github action checks in this list are required to merge a pull request
- check-success=Attributes
- check-success=Check
- check-success=Outpaths (aarch64-darwin)
- check-success=Outpaths (aarch64-linux)
- check-success=Outpaths (x86_64-darwin)
- check-success=Outpaths (x86_64-linux)
- check-success=Process
- check-success=Request
- check-success=Tag
- check-success=editorconfig-check
- check-success=label-pr
- check-success=nix-files-parseable-check
- check-success=nixfmt-check
- check-success=nixpkgs-vet
# queue up to 5 pull requests at a time
batch_size: 5
+3 -2
View File
@@ -157,6 +157,7 @@ nixos/modules/installer/tools/nix-fallback-paths.nix @NixOS/nix-team @raitobeza
# Python-related code and docs
/doc/languages-frameworks/python.section.md @mweinelt @natsukium
/maintainers/scripts/update-python-libraries @mweinelt @natsukium
/pkgs/by-name/up/update-python-libraries @mweinelt @natsukium
/pkgs/development/interpreters/python @mweinelt @natsukium
/pkgs/top-level/python-packages.nix @natsukium
/pkgs/top-level/release-python.nix @natsukium
@@ -206,8 +207,8 @@ nixos/modules/installer/tools/nix-fallback-paths.nix @NixOS/nix-team @raitobeza
# Browsers
/pkgs/applications/networking/browsers/firefox @mweinelt
/pkgs/applications/networking/browsers/chromium @emilylange
/nixos/tests/chromium.nix @emilylange
/pkgs/applications/networking/browsers/chromium @emilylange @networkException
/nixos/tests/chromium.nix @emilylange @networkException
# Certificate Authorities
pkgs/data/misc/cacert/ @ajs124 @lukegb @mweinelt
+8 -21
View File
@@ -58,7 +58,7 @@ Exit codes:
### Usage
This script can be used in GitHub Actions workflows as follows:
This script is implemented as a reusable GitHub Actions workflow, and can be used as follows:
```yaml
on: pull_request_target
@@ -67,32 +67,19 @@ on: pull_request_target
permissions: {}
jobs:
get-merge-commit:
# use the relative path of the get-merge-commit workflow yaml here
uses: ./.github/workflows/get-merge-commit.yml
build:
name: Build
runs-on: ubuntu-latest
needs: get-merge-commit
steps:
# Important: Because of `pull_request_target`, this doesn't check out the PR,
# but rather the base branch of the PR, which is needed so we don't run untrusted code
- uses: actions/checkout@<VERSION>
with:
path: base
sparse-checkout: ci
- name: Resolving the merge commit
env:
GH_TOKEN: ${{ github.token }}
run: |
if mergedSha=$(base/ci/get-merge-commit.sh ${{ github.repository }} ${{ github.event.number }}); then
echo "Checking the merge commit $mergedSha"
echo "mergedSha=$mergedSha" >> "$GITHUB_ENV"
else
# Skipping so that no notifications are sent
echo "Skipping the rest..."
fi
rm -rf base
- uses: actions/checkout@<VERSION>
# Add this to _all_ subsequent steps to skip them
if: env.mergedSha
if: needs.get-merge-commit.outputs.mergedSha
with:
ref: ${{ env.mergedSha }}
ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
- ...
```
+4 -1
View File
@@ -755,6 +755,9 @@ Used with Subversion. Expects `url` to a Subversion directory, `rev`, and `hash`
Used with Git. Expects `url` to a Git repo, `rev`, and `hash`. `rev` in this case can be full the git commit id (SHA1 hash) or a tag name like `refs/tags/v1.0`.
If you want to fetch a tag you should pass the `tag` parameter instead of `rev` which has the same effect as setting `rev = "refs/tags"/${version}"`.
This is safer than just setting `rev = version` w.r.t. possible branch and tag name conflicts.
Additionally, the following optional arguments can be given:
*`fetchSubmodules`* (Boolean)
@@ -833,7 +836,7 @@ A number of fetcher functions wrap part of `fetchurl` and `fetchzip`. They are m
## `fetchFromGitHub` {#fetchfromgithub}
`fetchFromGitHub` expects four arguments. `owner` is a string corresponding to the GitHub user or organization that controls this repository. `repo` corresponds to the name of the software repository. These are located at the top of every GitHub HTML page as `owner`/`repo`. `rev` corresponds to the Git commit hash or tag (e.g `v1.0`) that will be downloaded from Git. Finally, `hash` corresponds to the hash of the extracted directory. Again, other hash algorithms are also available, but `hash` is currently preferred.
`fetchFromGitHub` expects four arguments. `owner` is a string corresponding to the GitHub user or organization that controls this repository. `repo` corresponds to the name of the software repository. These are located at the top of every GitHub HTML page as `owner`/`repo`. `rev` corresponds to the Git commit hash or tag (e.g `v1.0`) that will be downloaded from Git. If you need to fetch a tag however, you should prefer to use the `tag` parameter which achieves this in a safer way with less boilerplate. Finally, `hash` corresponds to the hash of the extracted directory. Again, other hash algorithms are also available, but `hash` is currently preferred.
To use a different GitHub instance, use `githubBase` (defaults to `"github.com"`).
+1 -1
View File
@@ -364,7 +364,7 @@ including `nativeBuildInputs` to specify dependencies available to the `script`.
```nix
testers.runCommand {
name = "access-the-internet";
command = ''
script = ''
curl -o /dev/null https://example.com
touch $out
'';
+5 -17
View File
@@ -14,15 +14,13 @@ In Nixpkgs, `cargo-tauri.hook` overrides the default build and install phases.
rustPlatform,
fetchNpmDeps,
cargo-tauri,
darwin,
glib-networking,
libsoup,
nodejs,
npmHooks,
openssl,
pkg-config,
webkitgtk_4_0,
wrapGAppsHook3,
webkitgtk_4_1,
wrapGAppsHook4,
}:
rustPlatform.buildRustPackage rec {
@@ -47,25 +45,15 @@ rustPlatform.buildRustPackage rec {
# Make sure we can find our libraries
pkg-config
wrapGAppsHook3
wrapGAppsHook4
];
buildInputs =
[ openssl ]
++ lib.optionals stdenv.hostPlatform.isLinux [
glib-networking # Most Tauri apps need networking
libsoup
webkitgtk_4_0
]
++ lib.optionals stdenv.hostPlatform.isDarwin (
with darwin.apple_sdk.frameworks;
[
AppKit
CoreServices
Security
WebKit
]
);
webkitgtk_4_1
];
# Set our Tauri source directory
cargoRoot = "src-tauri";
+23 -17
View File
@@ -27,42 +27,48 @@ mkShell {
name = "dotnet-env";
packages = [
(with dotnetCorePackages; combinePackages [
sdk_6_0
sdk_7_0
sdk_8_0
sdk_9_0
])
];
}
```
This will produce a dotnet installation that has the dotnet 6.0 7.0 sdk. The first sdk listed will have it's cli utility present in the resulting environment. Example info output:
This will produce a dotnet installation that has the dotnet 8.0 9.0 sdk. The first sdk listed will have it's cli utility present in the resulting environment. Example info output:
```ShellSession
$ dotnet --info
.NET SDK:
Version: 7.0.202
Commit: 6c74320bc3
Version: 9.0.100
Commit: 59db016f11
Workload version: 9.0.100-manifests.3068a692
MSBuild version: 17.12.7+5b8665660
Runtime Environment:
OS Name: nixos
OS Version: 23.05
OS Version: 25.05
OS Platform: Linux
RID: linux-x64
Base Path: /nix/store/n2pm44xq20hz7ybsasgmd7p3yh31gnh4-dotnet-sdk-7.0.202/sdk/7.0.202/
Base Path: /nix/store/a03c70i7x6rjdr6vikczsp5ck3v6rixh-dotnet-sdk-9.0.100/share/dotnet/sdk/9.0.100/
.NET workloads installed:
There are no installed workloads to display.
Configured to use loose manifests when installing new manifests.
Host:
Version: 7.0.4
Version: 9.0.0
Architecture: x64
Commit: 0a396acafe
Commit: 9d5a6a9aa4
.NET SDKs installed:
6.0.407 [/nix/store/3b19303vwrhv0xxz1hg355c7f2hgxxgd-dotnet-core-combined/sdk]
7.0.202 [/nix/store/3b19303vwrhv0xxz1hg355c7f2hgxxgd-dotnet-core-combined/sdk]
8.0.404 [/nix/store/6wlrjiy10wg766490dcmp6x64zb1vc8j-dotnet-core-combined/share/dotnet/sdk]
9.0.100 [/nix/store/6wlrjiy10wg766490dcmp6x64zb1vc8j-dotnet-core-combined/share/dotnet/sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 6.0.15 [/nix/store/3b19303vwrhv0xxz1hg355c7f2hgxxgd-dotnet-core-combined/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 7.0.4 [/nix/store/3b19303vwrhv0xxz1hg355c7f2hgxxgd-dotnet-core-combined/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.15 [/nix/store/3b19303vwrhv0xxz1hg355c7f2hgxxgd-dotnet-core-combined/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 7.0.4 [/nix/store/3b19303vwrhv0xxz1hg355c7f2hgxxgd-dotnet-core-combined/shared/Microsoft.NETCore.App]
Microsoft.AspNetCore.App 8.0.11 [/nix/store/6wlrjiy10wg766490dcmp6x64zb1vc8j-dotnet-core-combined/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 9.0.0 [/nix/store/6wlrjiy10wg766490dcmp6x64zb1vc8j-dotnet-core-combined/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 8.0.11 [/nix/store/6wlrjiy10wg766490dcmp6x64zb1vc8j-dotnet-core-combined/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 9.0.0 [/nix/store/6wlrjiy10wg766490dcmp6x64zb1vc8j-dotnet-core-combined/share/dotnet/shared/Microsoft.NETCore.App]
Other architectures found:
None
@@ -146,8 +152,8 @@ in buildDotnetModule rec {
buildInputs = [ referencedProject ]; # `referencedProject` must contain `nupkg` in the folder structure.
dotnet-sdk = dotnetCorePackages.sdk_6_0;
dotnet-runtime = dotnetCorePackages.runtime_6_0;
dotnet-sdk = dotnetCorePackages.sdk_8_0;
dotnet-runtime = dotnetCorePackages.runtime_8_0;
executables = [ "foo" ]; # This wraps "$out/lib/$pname/foo" to `$out/bin/foo`.
executables = []; # Don't install any executables.
+11
View File
@@ -67,3 +67,14 @@ nix-shell -p 'julia.withPackages ["Plots"]' --run julia
This normally points at a special augmented version of the Julia [General packages registry](https://github.com/JuliaRegistries/General).
If you want to use a bleeding-edge version to pick up the latest package updates, you can plug in a later revision than the one in Nixpkgs.
* `juliaCpuTarget`: Allows you to set `JULIA_CPU_TARGET` when precompiling. Has no effect if `precompile=false`.
You may want to use this if you're building a Julia depot that will end up in a Nix cache and used on machines with
different CPUs.
Why? Julia will detect the CPU microarchitecture of the build machine and include this information in the precompiled
`*.ji` files. Starting in 1.10 Julia became more strict about checking the CPU target compatibility, so it may reject
your precompiled files if they were compiled on a different machine.
A good option to provide wide compatibility is to set this to `"generic"`, although this may reduce performance.
You can also set a semicolon-separated list of multiple different targets. See the Julia documentation for details.
+1 -1
View File
@@ -100,7 +100,7 @@ let
length head tail elem elemAt isList;
inherit (self.strings) concatStrings concatMapStrings concatImapStrings
stringLength substring isString replaceStrings
intersperse concatStringsSep concatMapStringsSep
intersperse concatStringsSep concatMapStringsSep concatMapAttrsStringSep
concatImapStringsSep concatLines makeSearchPath makeSearchPathOutput
makeLibraryPath makeIncludePath makeBinPath optionalString
hasInfix hasPrefix hasSuffix stringToCharacters stringAsChars escape
+48 -23
View File
@@ -306,35 +306,29 @@ in
As a result, directories with no `.nix` files (including empty
directories) will be transformed into empty attribute sets.
# Inputs
Structured function argument
: Attribute set containing the following attributes.
Additional attributes are ignored.
`callPackage`
: `pkgs.callPackage`
Type: `Path -> AttrSet -> a`
`directory`
: The directory to read package files from
Type: `Path`
# Type
```
packagesFromDirectoryRecursive :: AttrSet -> AttrSet
packagesFromDirectoryRecursive :: {
callPackage :: Path -> {} -> a,
directory :: Path,
...
} -> AttrSet
```
# Inputs
`callPackage`
: The function used to convert a Nix file's path into a leaf of the attribute set.
It is typically the `callPackage` function, taken from either `pkgs` or a new scope corresponding to the `directory`.
`directory`
: The directory to read package files from.
# Examples
:::{.example}
## `lib.filesystem.packagesFromDirectoryRecursive` usage example
## Basic use of `lib.packagesFromDirectoryRecursive`
```nix
packagesFromDirectoryRecursive {
@@ -342,17 +336,48 @@ in
directory = ./my-packages;
}
=> { ... }
```
In this case, `callPackage` will only search `pkgs` for a file's input parameters.
In other words, a file cannot refer to another file in the directory in its input parameters.
:::
::::{.example}
## Create a scope for the nix files found in a directory
```nix
lib.makeScope pkgs.newScope (
self: packagesFromDirectoryRecursive {
callPackage = self.callPackage;
inherit (self) callPackage;
directory = ./my-packages;
}
)
=> { ... }
```
For example, take the following directory structure:
```
my-packages
a.nix { b }: assert b ? b1; ...
b
b1.nix { a }: ...
b2.nix
```
Here, `b1.nix` can specify `{ a }` as a parameter, which `callPackage` will resolve as expected.
Likewise, `a.nix` receive an attrset corresponding to the contents of the `b` directory.
:::{.note}
`a.nix` cannot directly take as inputs packages defined in a child directory, such as `b1`.
:::
:::{.warning}
As of now, `lib.packagesFromDirectoryRecursive` cannot create nested scopes for sub-directories.
In particular, files under `b/` can only require (as inputs) other files under `my-packages`,
but not to those in the same directory, nor those in a parent directory; e.g, `b2.nix` cannot directly
require `b1`.
:::
::::
*/
packagesFromDirectoryRecursive =
{
+35 -5
View File
@@ -751,17 +751,47 @@ let
t' = opt.options.type;
mergedType = t.typeMerge t'.functor;
typesMergeable = mergedType != null;
typeSet = if (bothHave "type") && typesMergeable
then { type = mergedType; }
else {};
# TODO: Remove this when all downstream reliances of internals: 'functor.wrapped' are sufficiently migrated.
# A function that adds the deprecated wrapped message to a type.
addDeprecatedWrapped = t:
t // {
functor = t.functor // {
wrapped = t.functor.wrappedDeprecationMessage {
inherit loc;
};
};
};
typeSet =
if opt.options ? type then
if res ? type then
if typesMergeable then
{
type =
if mergedType ? functor.wrappedDeprecationMessage then
addDeprecatedWrapped mergedType
else
mergedType;
}
else
# Keep in sync with the same error below!
throw "The option `${showOption loc}' in `${opt._file}' is already declared in ${showFiles res.declarations}."
else if opt.options.type ? functor.wrappedDeprecationMessage then
{ type = addDeprecatedWrapped opt.options.type; }
else
{}
else
{};
bothHave = k: opt.options ? ${k} && res ? ${k};
in
if bothHave "default" ||
bothHave "example" ||
bothHave "description" ||
bothHave "apply" ||
(bothHave "type" && (! typesMergeable))
bothHave "apply"
then
# Keep in sync with the same error above!
throw "The option `${showOption loc}' in `${opt._file}' is already declared in ${showFiles res.declarations}."
else
let
+37
View File
@@ -269,6 +269,43 @@ rec {
f:
list: concatStringsSep sep (lib.imap1 f list);
/**
Like [`concatMapStringsSep`](#function-library-lib.strings.concatMapStringsSep)
but takes an attribute set instead of a list.
# Inputs
`sep`
: Separator to add between item strings
`f`
: Function that takes each key and value and return a string
`attrs`
: Attribute set to map from
# Type
```
concatMapAttrsStringSep :: String -> (String -> Any -> String) -> AttrSet -> String
```
# Examples
:::{.example}
## `lib.strings.concatMapAttrsStringSep` usage example
```nix
concatMapAttrsStringSep "\n" (name: value: "${name}: foo-${value}") { a = "0.1.0"; b = "0.2.0"; }
=> "a: foo-0.1.0\nb: foo-0.2.0"
```
:::
*/
concatMapAttrsStringSep =
sep: f: attrs:
concatStringsSep sep (lib.attrValues (lib.mapAttrs f attrs));
/**
Concatenate a list of strings, adding a newline at the end of each one.
Defined as `concatMapStrings (s: s + "\n")`.
+6
View File
@@ -39,6 +39,7 @@ let
composeManyExtensions
concatLines
concatMapAttrs
concatMapAttrsStringSep
concatMapStrings
concatStrings
concatStringsSep
@@ -328,6 +329,11 @@ runTests {
expected = "a,b,c";
};
testConcatMapAttrsStringSepExamples = {
expr = concatMapAttrsStringSep "\n" (name: value: "${name}: foo-${value}") { a = "0.1.0"; b = "0.2.0"; };
expected = "a: foo-0.1.0\nb: foo-0.2.0";
};
testConcatLines = {
expr = concatLines ["a" "b" "c"];
expected = "a\nb\nc\n";
+5
View File
@@ -386,6 +386,10 @@ checkConfigOutput '^true$' config.conditionalWorks ./declare-attrsOf.nix ./attrs
checkConfigOutput '^false$' config.conditionalWorks ./declare-lazyAttrsOf.nix ./attrsOf-conditional-check.nix
checkConfigOutput '^"empty"$' config.value.foo ./declare-lazyAttrsOf.nix ./attrsOf-conditional-check.nix
# Check attrsWith type merging
checkConfigError 'The option `mergedLazyNonLazy'\'' in `.*'\'' is already declared in `.*'\''\.' options.mergedLazyNonLazy ./lazy-attrsWith.nix
checkConfigOutput '^11$' config.lazyResult ./lazy-attrsWith.nix
checkConfigError 'infinite recursion encountered' config.nonLazyResult ./lazy-attrsWith.nix
# Even with multiple assignments, a type error should be thrown if any of them aren't valid
checkConfigError 'A definition for option .* is not of type .*' \
@@ -575,6 +579,7 @@ checkConfigOutput '^38|27$' options.submoduleLine38.declarationPositions.1.line
# nested options work
checkConfigOutput '^34$' options.nested.nestedLine34.declarationPositions.0.line ./declaration-positions.nix
cat <<EOF
====== module tests ======
$pass Pass
+57
View File
@@ -0,0 +1,57 @@
# Check that AttrsWith { lazy = true; } is lazy
{ lib, ... }:
let
inherit (lib) types mkOption;
lazyAttrsOf = mkOption {
# Same as lazyAttrsOf
type = types.attrsWith {
lazy = true;
elemType = types.int;
};
};
attrsOf = mkOption {
# Same as lazyAttrsOf
type = types.attrsWith {
elemType = types.int;
};
};
in
{
imports = [
# Module A
(
{ ... }:
{
options.mergedLazyLazy = lazyAttrsOf;
options.mergedLazyNonLazy = lazyAttrsOf;
options.mergedNonLazyNonLazy = attrsOf;
}
)
# Module B
(
{ ... }:
{
options.mergedLazyLazy = lazyAttrsOf;
options.mergedLazyNonLazy = attrsOf;
options.mergedNonLazyNonLazy = attrsOf;
}
)
# Result
(
{ config, ... }:
{
# Can only evaluate if lazy
config.mergedLazyLazy.bar = config.mergedLazyLazy.baz + 1;
config.mergedLazyLazy.baz = 10;
options.lazyResult = mkOption { default = config.mergedLazyLazy.bar; };
# Can not only evaluate if not lazy
config.mergedNonLazyNonLazy.bar = config.mergedNonLazyNonLazy.baz + 1;
config.mergedNonLazyNonLazy.baz = 10;
options.nonLazyResult = mkOption { default = config.mergedNonLazyNonLazy.bar; };
}
)
];
}
+76 -42
View File
@@ -83,11 +83,15 @@ rec {
# Default type merging function
# takes two type functors and return the merged type
defaultTypeMerge = f: f':
let mergedWrapped = f.wrapped.typeMerge f'.wrapped.functor;
mergedPayload = f.binOp f.payload f'.payload;
let
mergedWrapped = f.wrapped.typeMerge f'.wrapped.functor;
mergedPayload = f.binOp f.payload f'.payload;
hasPayload = assert (f'.payload != null) == (f.payload != null); f.payload != null;
hasWrapped = assert (f'.wrapped != null) == (f.wrapped != null); f.wrapped != null;
hasPayload = assert (f'.payload != null) == (f.payload != null); f.payload != null;
hasWrapped = assert (f'.wrapped != null) == (f.wrapped != null); f.wrapped != null;
typeFromPayload = if mergedPayload == null then null else f.type mergedPayload;
typeFromWrapped = if mergedWrapped == null then null else f.type mergedWrapped;
in
# Abort early: cannot merge different types
if f.name != f'.name
@@ -95,23 +99,23 @@ rec {
else
if hasPayload then
if hasWrapped then
# Just return the payload if returning wrapped is deprecated
if f ? wrappedDeprecationMessage then
typeFromPayload
else if hasWrapped then
# Has both wrapped and payload
throw ''
Type ${f.name} defines both `functor.payload` and `functor.wrapped` at the same time, which is not supported.
Use either `functor.payload` or `functor.wrapped` but not both.
If your code worked before remove `functor.payload` from the type definition.
If your code worked before remove either `functor.wrapped` or `functor.payload` from the type definition.
''
else
# Has payload
if mergedPayload == null then null else f.type mergedPayload
typeFromPayload
else
if hasWrapped then
# Has wrapped
# TODO(@hsjobeki): This could also be a warning and removed in the future
if mergedWrapped == null then null else f.type mergedWrapped
typeFromWrapped
else
f.type;
@@ -582,48 +586,78 @@ rec {
substSubModules = m: nonEmptyListOf (elemType.substSubModules m);
};
attrsOf = elemType: mkOptionType rec {
name = "attrsOf";
description = "attribute set of ${optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType}";
descriptionClass = "composite";
check = isAttrs;
merge = loc: defs:
mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) (zipAttrsWith (name: defs:
(mergeDefinitions (loc ++ [name]) elemType defs).optionalValue
)
# Push down position info.
(map (def: mapAttrs (n: v: { inherit (def) file; value = v; }) def.value) defs)));
emptyValue = { value = {}; };
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]);
getSubModules = elemType.getSubModules;
substSubModules = m: attrsOf (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; };
nestedTypes.elemType = elemType;
};
attrsOf = elemType: attrsWith { inherit elemType; };
# A version of attrsOf that's lazy in its values at the expense of
# conditional definitions not working properly. E.g. defining a value with
# `foo.attr = mkIf false 10`, then `foo ? attr == true`, whereas with
# attrsOf it would correctly be `false`. Accessing `foo.attr` would throw an
# error that it's not defined. Use only if conditional definitions don't make sense.
lazyAttrsOf = elemType: mkOptionType rec {
name = "lazyAttrsOf";
description = "lazy attribute set of ${optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType}";
lazyAttrsOf = elemType: attrsWith { inherit elemType; lazy = true; };
# base type for lazyAttrsOf and attrsOf
attrsWith =
let
# Push down position info.
pushPositions = map (def: mapAttrs (n: v: { inherit (def) file; value = v; }) def.value);
binOp = lhs: rhs:
let
elemType = lhs.elemType.typeMerge rhs.elemType.functor;
lazy =
if lhs.lazy == rhs.lazy then
lhs.lazy
else
null;
in
if elemType == null || lazy == null then
null
else
{
inherit elemType lazy;
};
in
{
elemType,
lazy ? false,
}:
mkOptionType {
name = if lazy then "lazyAttrsOf" else "attrsOf";
description = (if lazy then "lazy attribute set" else "attribute set") + " of ${optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType}";
descriptionClass = "composite";
check = isAttrs;
merge = loc: defs:
zipAttrsWith (name: defs:
let merged = mergeDefinitions (loc ++ [name]) elemType defs;
# mergedValue will trigger an appropriate error when accessed
in merged.optionalValue.value or elemType.emptyValue.value or merged.mergedValue
)
# Push down position info.
(map (def: mapAttrs (n: v: { inherit (def) file; value = v; }) def.value) defs);
merge = if lazy then (
# Lazy merge Function
loc: defs:
zipAttrsWith (name: defs:
let merged = mergeDefinitions (loc ++ [name]) elemType defs;
# mergedValue will trigger an appropriate error when accessed
in merged.optionalValue.value or elemType.emptyValue.value or merged.mergedValue
)
# Push down position info.
(pushPositions defs)
) else (
# Non-lazy merge Function
loc: defs:
mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) (zipAttrsWith (name: defs:
(mergeDefinitions (loc ++ [name]) elemType (defs)).optionalValue
)
# Push down position info.
(pushPositions defs)))
);
emptyValue = { value = {}; };
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]);
getSubModules = elemType.getSubModules;
substSubModules = m: lazyAttrsOf (elemType.substSubModules m);
functor = (defaultFunctor name) // { wrapped = elemType; };
substSubModules = m: attrsWith { elemType = elemType.substSubModules m; inherit lazy; };
functor = defaultFunctor "attrsWith" // {
wrappedDeprecationMessage = { loc }: lib.warn ''
The deprecated `type.functor.wrapped` attribute of the option `${showOption loc}` is accessed, use `type.nestedTypes.elemType` instead.
'' elemType;
payload = {
# Important!: Add new function attributes here in case of future changes
inherit elemType lazy;
};
inherit binOp;
};
nestedTypes.elemType = elemType;
};
+196 -83
View File
@@ -85,7 +85,7 @@
};
_0qq = {
email = "0qqw0qqw@gmail.com";
github = "0qq";
github = "0xKDI";
githubId = 64707304;
name = "Dmitry Kulikov";
};
@@ -155,7 +155,7 @@
};
_1000teslas = {
name = "Kevin Tran";
github = "1000teslas";
github = "sledgehammervampire";
githubId = 47207223;
};
_13r0ck = {
@@ -219,7 +219,7 @@
};
_404wolf = {
email = "wolfmermelstein@gmail.com";
github = "404wolf";
github = "404Wolf";
githubId = 108041238;
name = "Wolf Mermelstein";
};
@@ -537,6 +537,13 @@
githubId = 749381;
name = "Adam Tulinius";
};
adda = {
email = "chocholaty.david@protonmail.com";
matrix = "@adda0:matrix.org";
github = "adda0";
githubId = 52529234;
name = "David Chocholatý";
};
addict3d = {
email = "nickbathum@gmail.com";
matrix = "@nbathum:matrix.org";
@@ -792,7 +799,7 @@
};
ajgrf = {
email = "a@ajgrf.com";
github = "ajgrf";
github = "axgfn";
githubId = 10733175;
name = "Alex Griffin";
};
@@ -805,7 +812,7 @@
};
ajwhouse = {
email = "adam@ajwh.chat";
github = "ajwhouse";
github = "adam-dakota";
githubId = 56616368;
name = "Adam House";
};
@@ -1229,7 +1236,7 @@
};
amanse = {
email = "amansetiarjp@gmail.com";
github = "amanse";
github = "Amanse";
githubId = 13214574;
name = "Aman Setia";
};
@@ -1315,6 +1322,7 @@
};
amozeo = {
email = "wroclaw223@outlook.com";
github = "amozeo";
githubId = 37040543;
name = "Wroclaw";
};
@@ -1394,7 +1402,7 @@
};
andreasfelix = {
email = "fandreas@physik.hu-berlin.de";
github = "andreasfelix";
github = "felix-andreas";
githubId = 24651767;
name = "Felix Andreas";
};
@@ -1710,7 +1718,7 @@
};
aprl = {
email = "aprl@acab.dev";
github = "cutestnekoaqua";
github = "CutestNekoAqua";
githubId = 30842467;
name = "April John";
};
@@ -1787,13 +1795,13 @@
};
argrat = {
email = "n.bertazzo@protonmail.com";
github = "argrat";
github = "argo-v5";
githubId = 98821629;
name = "Nicolò Bertazzo";
};
arian-d = {
email = "arianxdehghani@gmail.com";
github = "arian-d";
github = "Arian-D";
githubId = 40076285;
name = "Arian Dehghani";
};
@@ -2022,7 +2030,7 @@
};
aspulse = {
email = "contact@aspulse.dev";
github = "aspulse";
github = "AsPulse";
githubId = 84216737;
name = "AsPulse / ";
keys = [ { fingerprint = "C919 E69E A7C0 E147 9E0F C26E 1EDA D0C6 70BD 062D"; } ];
@@ -2036,7 +2044,7 @@
};
astavie = {
email = "astavie@pm.me";
github = "astavie";
github = "Astavie";
githubId = 7745457;
name = "Astavie";
};
@@ -2292,7 +2300,7 @@
};
axertheaxe = {
email = "axertheaxe@proton.me";
github = "axertheaxe";
github = "AxerTheAxe";
githubId = 99703210;
name = "Katherine Jamison";
};
@@ -2425,7 +2433,7 @@
};
balssh = {
email = "george.bals25@gmail.com";
github = "balssh";
github = "Balssh";
githubId = 82440615;
name = "George Bals";
};
@@ -2497,6 +2505,13 @@
githubId = 6987136;
keys = [ { fingerprint = "2C6D 37D4 6AA1 DCDA BE8D F346 43E2 CF4C 01B9 4940"; } ];
};
BastianAsmussen = {
name = "Bastian Asmussen";
email = "bastian@asmussen.tech";
github = "BastianAsmussen";
githubId = 76102128;
keys = [ { fingerprint = "3B11 7469 0893 85E7 16C2 7CD9 0FE5 A355 DBC9 2568"; } ];
};
basvandijk = {
email = "v.dijk.bas@gmail.com";
github = "basvandijk";
@@ -2654,7 +2669,7 @@
ben9986 = {
name = "Ben Carmichael";
email = "ben9986.unvmn@passinbox.com";
github = "ben9986";
github = "Ben9986";
githubId = 38633150;
keys = [ { fingerprint = "03C7 A587 74B3 F0E8 CE1F 4F8E ABBC DD77 69BC D3B0"; } ];
};
@@ -2863,7 +2878,7 @@
};
bhasherbel = {
email = "nixos.maintainer@bhasher.com";
github = "bhasherbel";
github = "BhasherBEL";
githubId = 45831883;
name = "Brieuc Dubois";
};
@@ -3017,7 +3032,7 @@
};
blusk = {
email = "bluskript@gmail.com";
github = "Bluskript";
github = "bluskript";
githubId = 52386117;
name = "Blusk";
};
@@ -3373,7 +3388,7 @@
};
ByteSudoer = {
email = "bytesudoer@gmail.com";
github = "bytesudoer";
github = "ByteSudoer";
githubId = 88513682;
name = "ByteSudoer";
};
@@ -3549,7 +3564,7 @@
};
campadrenalin = {
email = "campadrenalin@gmail.com";
github = "campadrenalin";
github = "MaddieM4";
githubId = 289492;
name = "Philip Horger";
};
@@ -3887,7 +3902,7 @@
};
cherrykitten = {
email = "contact@cherrykitten.dev";
github = "cherrykitten";
github = "CherryKitten";
githubId = 20300586;
matrix = "@sammy:cherrykitten.dev";
name = "CherryKitten";
@@ -4018,7 +4033,7 @@
};
chrispwill = {
email = "chris@chrispwill.com";
github = "chrispwill";
github = "ChrisPWill";
githubId = 271099;
name = "Chris Williams";
};
@@ -4110,7 +4125,7 @@
};
citadelcore = {
email = "alex@arctarus.co.uk";
github = "VertexA115";
github = "RealityAnomaly";
githubId = 5567402;
name = "Alex Zero";
keys = [ { fingerprint = "A0AA 4646 B8F6 9D45 4553 5A88 A515 50ED B450 302C"; } ];
@@ -4376,7 +4391,7 @@
colamaroro = {
name = "Corentin Rondier";
email = "github@rondier.io";
github = "colamaroro";
github = "COLAMAroro";
githubId = 12484955;
matrix = "@colamaroro:lovelyrad.io";
};
@@ -4416,7 +4431,7 @@
};
coloquinte = {
email = "gabriel.gouvine_nix@m4x.org";
github = "coloquinte";
github = "Coloquinte";
githubId = 4102525;
name = "Gabriel Gouvine";
};
@@ -4472,7 +4487,7 @@
connorbaker = {
email = "connor.baker@tweag.io";
matrix = "@connorbaker:matrix.org";
github = "connorbaker";
github = "ConnorBaker";
name = "Connor Baker";
githubId = 3880346;
};
@@ -4597,7 +4612,7 @@
};
crem = {
email = "max@alstercaminer.com";
github = "MaxCaminer";
github = "ItsCrem";
githubId = 16345077;
name = "crem";
};
@@ -4635,7 +4650,7 @@
croissong = {
email = "jan.moeller0@pm.me";
name = "Jan Möller";
github = "Croissong";
github = "croissong";
githubId = 4162215;
keys = [ { fingerprint = "CE97 9DEE 904C 26AA 3716 78C2 96A4 38F9 EE72 572F"; } ];
};
@@ -4653,6 +4668,13 @@
name = "Carl Richard Theodor Schneider";
keys = [ { fingerprint = "2017 E152 BB81 5C16 955C E612 45BC C1E2 709B 1788"; } ];
};
cryo = {
email = "cryo@disroot.org";
github = "cry0ice";
githubId = 176274027;
name = "Cryo";
keys = [ { fingerprint = "2CF7 F8E8 2258 5751 2591 F97F 4B12 E34A 25A9 AB35"; } ];
};
Cryolitia = {
name = "Cryolitia PukNgae";
email = "Cryolitia@gmail.com";
@@ -4689,6 +4711,12 @@
githubId = 490965;
name = "Craig Swank";
};
cterence = {
email = "terence.chateigne@posteo.net";
github = "cterence";
githubId = 25285508;
name = "Térence Chateigné";
};
ctron = {
email = "ctron@dentrassi.de";
github = "ctron";
@@ -4779,6 +4807,12 @@
github = "d4ilyrun";
githubId = 34611103;
};
d4rk = {
name = "Anoop Menon";
email = "d4rk@blackvoltage.org";
github = "d4rk";
githubId = 22163;
};
d4rkstar = {
name = "Bruno Salzano";
email = "d4rkstar@gmail.com";
@@ -5126,7 +5160,7 @@
};
dawoox = {
email = "contact@antoinebellanger.fr";
github = "dawoox";
github = "Dawoox";
githubId = 48325941;
name = "Dawoox";
};
@@ -5224,7 +5258,7 @@
declan = {
name = "Declan Rixon";
email = "declan.fraser.rixon@gmail.com";
github = "DeclanRixon";
github = "DeclanBaggins";
githubId = 57464835;
};
deeengan = {
@@ -5242,7 +5276,7 @@
};
deekahy = {
email = "Lennart.Diego.Kahn@gmail.com";
github = "deekahy";
github = "DeeKahy";
githubId = 97156953;
name = "Lennart Diego Kahn";
};
@@ -5343,7 +5377,7 @@
};
demine = {
email = "riches_tweaks0o@icloud.com";
github = "demine0";
github = "Demine0";
githubId = 51992962;
name = "Nikita Demin";
};
@@ -5361,7 +5395,7 @@
};
denperidge = {
email = "contact@denperidge.com";
github = "denperidge";
github = "Denperidge";
githubId = 27348469;
name = "Cat";
};
@@ -5373,7 +5407,7 @@
};
derdennisop = {
email = "dennish@wuitz.de";
github = "derdennisop";
github = "DerDennisOP";
githubId = 52411861;
name = "Dennis";
};
@@ -5539,13 +5573,13 @@
};
dgollings = {
email = "daniel.gollings+nixpkgs@gmail.com";
github = "dgollings";
github = "DGollings";
githubId = 2032823;
name = "Daniel Gollings";
};
dgonyeo = {
email = "derek@gonyeo.com";
github = "dgonyeo";
github = "cgonyeo";
githubId = 2439413;
name = "Derek Gonyeo";
};
@@ -5578,6 +5612,12 @@
githubId = 15774340;
name = "Thomas Depierre";
};
DictXiong = {
email = "me@beardic.cn";
github = "DictXiong";
githubId = 41772157;
name = "Dict Xiong";
};
diegolelis = {
email = "diego.o.lelis@gmail.com";
github = "DiegoLelis";
@@ -6093,7 +6133,7 @@
};
dwrege = {
email = "email@dwrege.de";
github = "dominicwrege";
github = "DominicWrege";
githubId = 7389000;
name = "Dominic Wrege";
};
@@ -6236,7 +6276,7 @@
};
edeneast = {
email = "edenofest@gmail.com";
github = "edeneast";
github = "EdenEast";
githubId = 2746374;
name = "edeneast";
};
@@ -6340,6 +6380,12 @@
githubId = 701128;
name = "Eike Kettner";
};
eilvelia = {
email = "hi@eilvelia.cat";
github = "eilvelia";
githubId = 10106819;
name = "eilvelia";
};
eken = {
email = "edvin.kallstrom@protonmail.com";
github = "Eken-beep";
@@ -6663,7 +6709,7 @@
};
ercao = {
email = "vip@ercao.cn";
github = "ercao";
github = "O8888";
githubId = 51725284;
name = "ercao";
keys = [ { fingerprint = "F3B0 36F7 B0CB 0964 3C12 D3C7 FFAB D125 7ECF 0889"; } ];
@@ -6685,7 +6731,7 @@
erethon = {
email = "dgrig@erethon.com";
matrix = "@dgrig:erethon.com";
github = "erethon";
github = "Erethon";
githubId = 1254842;
name = "Dionysis Grigoropoulos";
};
@@ -6919,7 +6965,7 @@
};
eveeifyeve = {
name = "Eveeifyeve";
github = "eveeifyeve";
github = "Eveeifyeve";
githubId = 88671402;
matrix = "@eveeifyeve:matrix.org";
email = "eveeg1971@gmail.com";
@@ -6970,7 +7016,7 @@
};
ewuuwe = {
email = "ewu.uweq@pm.me";
github = "ewuuwe";
github = "EwuUwe";
githubId = 63652646;
name = "Xaver Oswald";
};
@@ -7064,7 +7110,7 @@
};
fabianrig = {
email = "fabianrig@posteo.de";
github = "fabianrig";
github = "FabianRig";
githubId = 88741530;
name = "Fabian Rigoll";
};
@@ -7606,7 +7652,7 @@
name = "Christian Friedow";
};
friedrichaltheide = {
github = "friedrichaltheide";
github = "FriedrichAltheide";
githubId = 11352905;
name = "Friedrich Altheide";
};
@@ -8052,7 +8098,7 @@
name = "Priyanshu Tripathi";
email = "nixos@getpsyched.dev";
matrix = "@getpsyched:matrix.org";
github = "getpsyched";
github = "GetPsyched";
githubId = 43472218;
};
getreu = {
@@ -8115,7 +8161,7 @@
};
gigglesquid = {
email = "jack.connors@protonmail.com";
github = "gigglesquid";
github = "GiggleSquid";
githubId = 3685154;
name = "Jack connors";
keys = [ { fingerprint = "21DF 8034 B212 EDFF 9F19 9C19 F65B 7583 7ABF D019"; } ];
@@ -8525,6 +8571,12 @@
githubId = 6578603;
name = "Jonas Rembser";
};
gurjaka = {
name = "Gurami Esartia";
email = "esartia.gurika@gmail.com";
github = "Gurjaka";
githubId = 143032436;
};
guserav = {
github = "guserav";
githubId = 28863828;
@@ -8627,7 +8679,7 @@
};
hannesgith = {
email = "nix@h-h.win";
github = "hannesgith";
github = "HannesGitH";
githubId = 33062605;
name = "Hannes Hattenbach";
};
@@ -8666,7 +8718,7 @@
harbiinger = {
email = "theo.godin@protonmail.com";
matrix = "@hrbgr:matrix.org";
github = "harbiinger";
github = "Harbiinger";
githubId = 55398594;
name = "Theo Godin";
};
@@ -8950,7 +9002,7 @@
};
hhydraa = {
email = "hcurfman@keemail.me";
github = "hhydraa";
github = "hcur";
githubId = 58676303;
name = "hhydraa";
};
@@ -9085,7 +9137,7 @@
};
hornwall = {
email = "hannes@hornwall.me";
github = "hornwall";
github = "Hornwall";
githubId = 1064477;
name = "Hannes Hornwall";
};
@@ -9191,7 +9243,7 @@
};
humancalico = {
email = "humancalico@disroot.org";
github = "humancalico";
github = "akshatagarwl";
githubId = 51334444;
name = "Akshat Agarwal";
};
@@ -9616,7 +9668,7 @@
iogamaster = {
email = "iogamastercode+nixpkgs@gmail.com";
name = "IogaMaster";
github = "iogamaster";
github = "IogaMaster";
githubId = 67164465;
};
ionutnechita = {
@@ -9684,7 +9736,7 @@
istoph = {
email = "chr@istoph.de";
name = "Christoph Hüffelmann";
github = "istoph";
github = "istof";
githubId = 114227790;
};
istudyatuni = {
@@ -9999,7 +10051,7 @@
janlikar = {
name = "Jan Likar";
email = "jan.likar@protonmail.com";
github = "janlikar";
github = "JanLikar";
githubId = 4228250;
};
jansol = {
@@ -10058,7 +10110,7 @@
jayesh-bhoot = {
name = "Jayesh Bhoot";
email = "jb@jayeshbhoot.com";
github = "jyssh";
github = "jbhoot";
githubId = 1915507;
};
jayman2000 = {
@@ -10236,7 +10288,7 @@
};
jedsek = {
email = "jedsek@qq.com";
github = "jedsek";
github = "Jedsek";
githubId = 63626406;
name = "Jedsek";
};
@@ -10512,6 +10564,12 @@
githubId = 1058504;
name = "José Luis Lafuente";
};
jljox = {
email = "jeanluc.jox@gmail.com";
github = "jljox";
githubId = 3665886;
name = "Jean-Luc Jox";
};
jloyet = {
email = "ml@fatbsd.com";
github = "fatpat";
@@ -11823,7 +11881,7 @@
knarkzel = {
email = "knarkzel@gmail.com";
name = "Knarkzel";
github = "Knarkzel";
github = "svelterust";
githubId = 85593302;
};
knedlsepp = {
@@ -12375,7 +12433,7 @@
name = "Jacob LeCoq";
email = "lecoqjacob@gmail.com";
githubId = 9278174;
github = "lecoqjacob";
github = "bayou-brogrammer";
keys = [ { fingerprint = "C505 1E8B 06AC 1776 6875 1B60 93AF DAD0 10B3 CB8D"; } ];
};
ledif = {
@@ -12467,7 +12525,7 @@
};
leonid = {
email = "belyaev.l@northeastern.edu";
github = "leonidbelyaev";
github = "aeblyve";
githubId = 77865363;
name = "Leonid Belyaev";
};
@@ -12573,7 +12631,7 @@
};
libjared = {
email = "jared@perrycode.com";
github = "libjared";
github = "hypevhs";
githubId = 3746656;
matrix = "@libjared:matrix.org";
name = "Jared Perry";
@@ -12630,7 +12688,7 @@
};
lillycham = {
email = "lillycat332@gmail.com";
github = "lillycat332";
github = "lillycham";
githubId = 54189319;
name = "Lilly Cham";
};
@@ -12783,7 +12841,7 @@
logo = {
email = "logo4poop@protonmail.com";
matrix = "@logo4poop:matrix.org";
github = "logo4poop";
github = "l0go";
githubId = 24994565;
name = "Isaac Silverstein";
};
@@ -12851,6 +12909,13 @@
githubId = 1202012;
name = "Ignat Loskutov";
};
lostmsu = {
email = "lostfreeman@gmail.com";
github = "lostmsu";
githubId = 239520;
matrix = "@lostmsu:matrix.org";
name = "Victor Nova";
};
lostnet = {
email = "lost.networking@gmail.com";
github = "lostnet";
@@ -12949,7 +13014,7 @@
};
lu15w1r7h = {
email = "lwirth2000@gmail.com";
github = "LU15W1R7H";
github = "luiswirth";
githubId = 37505890;
name = "Luis Wirth";
};
@@ -14794,7 +14859,7 @@
};
modderme123 = {
email = "modderme123@gmail.com";
github = "modderme123";
github = "milomg";
githubId = 14153763;
name = "modderme123";
};
@@ -15140,7 +15205,7 @@
};
mtrsk = {
email = "marcos.schonfinkel@protonmail.com";
github = "mtrsk";
github = "schonfinkel";
githubId = 16356569;
name = "Marcos Benevides";
};
@@ -15339,7 +15404,7 @@
Name = {
name = "Name";
email = "lasagna@garfunkles.space";
github = "namescode";
github = "NamesCode";
githubId = 86119896;
};
name-snrl = {
@@ -15394,7 +15459,7 @@
name = "Nathan Bijnens";
};
nathanielbrough = {
github = "silvergasp";
github = "nathaniel-brough";
githubId = 7277663;
email = "nathaniel.brough@gmail.com";
name = "Nathaniel Brough";
@@ -15460,7 +15525,7 @@
nbsp = {
email = "aoife@enby.space";
matrix = "@nbsp:enby.space";
github = "enbyspace";
github = "nbsp";
githubId = 57151943;
name = "aoife cassidy";
};
@@ -15565,7 +15630,7 @@
};
nerdypepper = {
email = "nerdy@peppe.rs";
github = "nerdypepper";
github = "oppiliappan";
githubId = 23743547;
name = "Akshay Oppiliappan";
};
@@ -16573,7 +16638,7 @@
};
osener = {
email = "ozan@ozansener.com";
github = "osener";
github = "ozanmakes";
githubId = 111265;
name = "Ozan Sener";
};
@@ -16636,7 +16701,7 @@
};
oxapentane = {
email = "blame@oxapentane.com";
github = "oxapentane";
github = "gshipunov";
githubId = 1297357;
name = "Grigory Shipunov";
keys = [ { fingerprint = "DD09 98E6 CDF2 9453 7FC6 04F9 91FA 5E5B F9AA 901C"; } ];
@@ -16712,7 +16777,7 @@
};
paddygord = {
email = "pgpatrickgordon@gmail.com";
github = "avaunit02";
github = "avagordon01";
githubId = 10776658;
name = "Patrick Gordon";
};
@@ -16766,6 +16831,11 @@
githubId = 686076;
name = "Vitalii Voloshyn";
};
pancaek = {
github = "pancaek";
githubId = 20342389;
name = "paneku";
};
panda2134 = {
email = "me+nixpkgs@panda2134.site";
github = "panda2134";
@@ -16875,7 +16945,7 @@
};
patricksjackson = {
email = "patrick@jackson.dev";
github = "patricksjackson";
github = "arcuru";
githubId = 160646;
name = "Patrick Jackson";
};
@@ -16929,7 +16999,7 @@
};
pawelpacana = {
email = "pawel.pacana@gmail.com";
github = "pawelpacana";
github = "mostlyobvious";
githubId = 116740;
name = "Paweł Pacana";
};
@@ -17092,7 +17162,7 @@
};
petercommand = {
email = "petercommand@gmail.com";
github = "petercommand";
github = "ptrcmd";
githubId = 1260660;
name = "petercommand";
};
@@ -17179,7 +17249,7 @@
name = "Léo Lavaur";
email = "phdcybersec@pm.me";
github = "phdcybersec";
github = "leolavaur";
githubId = 82591009;
keys = [ { fingerprint = "7756 E88F 3C6A 47A5 C5F0 CDFB AB54 6777 F93E 20BF"; } ];
};
@@ -18507,7 +18577,7 @@
realsnick = {
name = "Ido Samuelson";
email = "ido.samuelson@gmail.com";
github = "realsnick";
github = "i-am-logger";
githubId = 1440852;
};
reckenrode = {
@@ -19539,6 +19609,14 @@
github = "Sail0rd";
githubId = 55802415;
};
sako = {
name = "Sako";
email = "sako@cock.email";
matrix = "@sako:imagisphe.re";
github = "Sakooooo";
githubId = 78461130;
keys = [ { fingerprint = "CA52 EE7B E681 720E 32B6 6792 FE52 FD65 B76E 4751"; } ];
};
samalws = {
email = "sam@samalws.com";
name = "Sam Alws";
@@ -19653,7 +19731,7 @@
};
sanana = {
email = "asya@waifu.club";
github = "AsyaTheAbove";
github = "lunabunja";
githubId = 40492846;
keys = [ { fingerprint = "B766 7717 1644 5ABC DE82 94AA 4679 BF7D CC04 4783"; } ];
name = "sanana the skenana";
@@ -19793,7 +19871,7 @@
};
schinmai-akamai = {
email = "schinmai@akamai.com";
github = "schinmai-akamai";
github = "tchinmai7";
githubId = 70169773;
name = "Tarun Chinmai Sekar";
};
@@ -19866,7 +19944,7 @@
};
scoder12 = {
name = "Spencer Pogorzelski";
github = "Scoder12";
github = "spencerpogo";
githubId = 34356756;
};
scolobb = {
@@ -20233,6 +20311,12 @@
githubId = 293035;
name = "Shawn Dellysse";
};
ShawnToubeau = {
name = "Shawn Toubeau";
email = "shawntoubeau@gmail.com";
github = "ShawnToubeau";
githubId = 22332636;
};
shayne = {
email = "shaynesweeney@gmail.com";
github = "shayne";
@@ -20704,7 +20788,7 @@
};
smironov = {
email = "grrwlf@gmail.com";
github = "grwlf";
github = "sergei-mironov";
githubId = 4477729;
name = "Sergey Mironov";
};
@@ -21101,6 +21185,12 @@
github = "stasjok";
githubId = 1353637;
};
StayBlue = {
name = "StayBlue";
email = "blue@spook.rip";
github = "StayBlue";
githubId = 23127866;
};
steamwalker = {
email = "steamwalker@xs4all.nl";
github = "steamwalker";
@@ -21638,6 +21728,17 @@
githubId = 6064962;
name = "TakWolf";
};
talhaHavadar = {
email = "havadartalha@gmail.com";
github = "talhaHavadar";
githubId = 6908462;
name = "Talha Can Havadar";
keys = [
{
fingerprint = "1E13 12DF 4B71 58B6 EBF9 DE78 2574 3879 62FE B0D1";
}
];
};
talkara = {
email = "taito.horiuchi@relexsolutions.com";
github = "talkara";
@@ -21826,6 +21927,12 @@
githubId = 2084639;
name = "Manu";
};
tensor5 = {
github = "tensor5";
githubId = 1545895;
matrix = "@tensor5:matrix.org";
name = "Nicola Squartini";
};
teozkr = {
email = "teo@nullable.se";
github = "nightkr";
@@ -22076,7 +22183,7 @@
};
therealr5 = {
email = "rouven@rfive.de";
github = "therealr5";
github = "rouven0";
githubId = 72568063;
name = "Rouven Seifert";
keys = [ { fingerprint = "1169 87A8 DD3F 78FF 8601 BF4D B95E 8FE6 B11C 4D09"; } ];
@@ -22513,7 +22620,7 @@
tomsiewert = {
email = "tom@siewert.io";
matrix = "@tom:frickel.earth";
github = "tomsiewert";
github = "sinuscosinustan";
githubId = 8794235;
name = "Tom Siewert";
};
@@ -22579,6 +22686,12 @@
githubId = 2125828;
name = "Alex Davies";
};
travgm = {
email = "travis@travgm.org";
github = "travgm";
githubId = 99630881;
name = "Travis Montoya";
};
travisbhartwell = {
email = "nafai@travishartwell.net";
github = "travisbhartwell";
@@ -23416,7 +23529,7 @@
};
voidless = {
email = "julius.schmitt@yahoo.de";
github = "voidIess";
github = "bratorange";
githubId = 45292658;
name = "Julius Schmitt";
};
@@ -0,0 +1 @@
/target
@@ -0,0 +1,9 @@
[package]
name = "check-maintainers"
version = "0.1.0"
edition = "2021"
[dependencies]
octocrab = "0.42.1"
serde_json = "1.0.133"
tokio = { version = "1.41.1", features = ["fs", "macros", "rt-multi-thread"] }
@@ -0,0 +1,42 @@
use std::{env, error::Error, io::{self, Write}};
use tokio::fs;
#[tokio::main]
async fn main() {
real_main().await.unwrap();
}
async fn real_main() -> Result<(), Box<dyn Error>> {
let gh = octocrab::OctocrabBuilder::default()
.personal_token(env::var("GITHUB_PAT").expect("no GITHUB_PAT configured"))
.build()?;
// first run: nix-instantiate -A lib.maintainers --eval --strict --json > maintainers.json
let data: serde_json::Value = serde_json::from_str(&fs::read_to_string("maintainers.json").await?)?;
let data = data.as_object().unwrap();
let total = data.len();
for (i, (maintainer_name, v)) in data.iter().enumerate() {
print!("\r{i}/{total}");
io::stdout().flush()?;
let Some(github_id) = v.get("githubId") else {
println!("\rWARN: no id for {maintainer_name}");
continue;
};
let Some(github) = v.get("github") else {
println!("\rWARN: no username for {maintainer_name}");
continue;
};
let Ok(name) = gh.users_by_id(github_id.as_u64().unwrap()).profile().await else {
println!("\rWARN: error fetching {maintainer_name}");
continue;
};
let name = name.login;
if name.to_ascii_lowercase() != github.as_str().unwrap().to_ascii_lowercase() {
println!("\rERR: {maintainer_name} has github = {github}, but username really is '{name}'");
}
}
Ok(())
}
+1 -1
View File
@@ -1,3 +1,3 @@
#!/usr/bin/env nix-shell
#!nix-shell -I nixpkgs=channel:nixpkgs-unstable -i bash -p "python3.withPackages (ps: with ps; [ packaging requests ])" -p nix-prefetch-git
exec python3 pkgs/development/interpreters/python/update-python-libraries/update-python-libraries.py $@
exec python3 pkgs/by-name/up/update-python-libraries/update-python-libraries.py $@
@@ -399,6 +399,23 @@ Composed types are types that take a type as parameter. `listOf
returned instead for the same `mkIf false` definition.
:::
`types.attrsWith` { *`elemType`*, *`lazy`* ? false }
: An attribute set of where all the values are of *`elemType`* type.
**Parameters**
`elemType` (Required)
: Specifies the type of the values contained in the attribute set.
`lazy`
: Determines whether the attribute set is lazily evaluated. See: `types.lazyAttrsOf`
**Behavior**
- `attrsWith { elemType = t; }` is equivalent to `attrsOf t`
- `attrsWith { lazy = true; elemType = t; }` is equivalent to `lazyAttrsOf t`
`types.uniq` *`t`*
: Ensures that type *`t`* cannot be merged. It is used to ensure option
@@ -109,7 +109,7 @@ In addition to numerous new and upgraded packages, this release has the followin
- [Maddy](https://maddy.email/), a free an open source mail server. Available as [services.maddy](#opt-services.maddy.enable).
- [matrix-conduit](https://conduit.rs/), a simple, fast and reliable chat server powered by matrix. Available as [services.matrix-conduit](option.html#opt-services.matrix-conduit.enable).
- [matrix-conduit](https://conduit.rs/), a simple, fast and reliable chat server powered by matrix. Available as [services.matrix-conduit](options.html#opt-services.matrix-conduit.enable).
- [Moosefs](https://moosefs.com), fault tolerant petabyte distributed file system. Available as [moosefs](#opt-services.moosefs.master.enable).
@@ -173,11 +173,11 @@
- [Suricata](https://suricata.io/), a free and open source, mature, fast and robust network threat detection engine. Available as [services.suricata](options.html#opt-services.suricata.enable).
- [Playerctld](https://github.com/altdesktop/playerctl), a daemon to track media player activity. Available as [services.playerctld](option.html#opt-services.playerctld.enable).
- [Playerctld](https://github.com/altdesktop/playerctl), a daemon to track media player activity. Available as [services.playerctld](options.html#opt-services.playerctld.enable).
- [Glance](https://github.com/glanceapp/glance), a self-hosted dashboard that puts all your feeds in one place. Available as [services.glance](option.html#opt-services.glance.enable).
- [Glance](https://github.com/glanceapp/glance), a self-hosted dashboard that puts all your feeds in one place. Available as [services.glance](options.html#opt-services.glance.enable).
- [Apache Tika](https://github.com/apache/tika), a toolkit that detects and extracts metadata and text from over a thousand different file types. Available as [services.tika](option.html#opt-services.tika.enable).
- [Apache Tika](https://github.com/apache/tika), a toolkit that detects and extracts metadata and text from over a thousand different file types. Available as [services.tika](options.html#opt-services.tika.enable).
- [Misskey](https://misskey-hub.net/en/), an interplanetary microblogging platform. Available as [services.misskey](options.html#opt-services.misskey.enable).
@@ -199,7 +199,7 @@
- [ToDesk](https://www.todesk.com/linux.html), a remote desktop application. Available as [services.todesk](#opt-services.todesk.enable).
- [Dependency Track](https://dependencytrack.org/), an intelligent Component Analysis platform that allows organizations to identify and reduce risk in the software supply chain. Available as [services.dependency-track](option.html#opt-services.dependency-track.enable).
- [Dependency Track](https://dependencytrack.org/), an intelligent Component Analysis platform that allows organizations to identify and reduce risk in the software supply chain. Available as [services.dependency-track](options.html#opt-services.dependency-track.enable).
- [Immich](https://github.com/immich-app/immich), a self-hosted photo and video backup solution. Available as [services.immich](#opt-services.immich.enable).
@@ -215,9 +215,9 @@
- [Swapspace](https://github.com/Tookmund/Swapspace), a dynamic swap space manager that turns your unused free space into swap automatically. Available as [services.swapspace](#opt-services.swapspace.enable).
- [Zapret](https://github.com/bol-van/zapret), a DPI bypass tool. Available as [services.zapret](option.html#opt-services.zapret.enable).
- [Zapret](https://github.com/bol-van/zapret), a DPI bypass tool. Available as [services.zapret](options.html#opt-services.zapret.enable).
- [Glances](https://github.com/nicolargo/glances), an open-source system cross-platform monitoring tool. Available as [services.glances](option.html#opt-services.glances).
- [Glances](https://github.com/nicolargo/glances), an open-source system cross-platform monitoring tool. Available as [services.glances](options.html#opt-services.glances).
## Backward Incompatibilities {#sec-release-24.11-incompatibilities}
@@ -12,13 +12,19 @@
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
- [Kimai](https://www.kimai.org/), a web-based multi-user time-tracking application. Available as [services.kimai](option.html#opt-services.kimai).
- [Kimai](https://www.kimai.org/), a web-based multi-user time-tracking application. Available as [services.kimai](options.html#opt-services.kimai).
- [Omnom](https://github.com/asciimoo/omnom), a webpage bookmarking and snapshotting service. Available as [services.omnom](options.html#opt-services.omnom.enable).
- [Amazon CloudWatch Agent](https://github.com/aws/amazon-cloudwatch-agent), the official telemetry collector for AWS CloudWatch and AWS X-Ray. Available as [services.amazon-cloudwatch-agent](#opt-services.amazon-cloudwatch-agent.enable).
- [Traccar](https://www.traccar.org/), a modern GPS Tracking Platform. Available as [services.traccar](#opt-services.traccar.enable).
- [agorakit](https://github.com/agorakit/agorakit), an organization tool for citizens' collectives. Available with [services.agorakit](#opt-services.agorakit.enable).
- [Amazon CloudWatch Agent](https://github.com/aws/amazon-cloudwatch-agent), the official telemetry collector for AWS CloudWatch and AWS X-Ray. Available as [services.amazon-cloudwatch-agent](options.html#opt-services.amazon-cloudwatch-agent.enable).
- [agorakit](https://github.com/agorakit/agorakit), an organization tool for citizens' collectives. Available with [services.agorakit](options.html#opt-services.agorakit.enable).
- [mqtt-exporter](https://github.com/kpetremann/mqtt-exporter/), a Prometheus exporter for exposing messages from MQTT. Available as [services.prometheus.exporters.mqtt](#opt-services.prometheus.exporters.mqtt.enable).
- [Buffyboard](https://gitlab.postmarketos.org/postmarketOS/buffybox/-/tree/master/buffyboard), a framebuffer on-screen keyboard. Available as [services.buffyboard](option.html#opt-services.buffyboard).
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
@@ -31,6 +37,9 @@
- `buildGoPackage` has been removed. Use `buildGoModule` instead. See the [Go section in the nixpkgs manual](https://nixos.org/manual/nixpkgs/unstable/#sec-language-go) for details.
- `strawberry` has been updated to 1.2, which drops support for the VLC backend and Qt 5. The `strawberry-qt5` package
and `withGstreamer`/`withVlc` override options have been removed due to this.
- `timescaledb` requires manual upgrade steps.
After you run ALTER EXTENSION, you must run [this SQL script](https://github.com/timescale/timescaledb-extras/blob/master/utils/2.15.X-fix_hypertable_foreign_keys.sql). For more details, see the following pull requests [#6797](https://github.com/timescale/timescaledb/pull/6797).
PostgreSQL 13 is no longer supported in TimescaleDB v2.16.
@@ -39,8 +48,18 @@
- `zammad` has had its support for MySQL removed, since it was never working correctly and is now deprecated upstream. Check the [migration guide](https://docs.zammad.org/en/latest/appendix/migrate-to-postgresql.html) for how to convert your database to PostgreSQL.
- `nodePackages.insect` has been removed, as it's deprecated by upstream. The suggested replacement is `numbat`.
- `nodePackages.webpack-dev-server` has been removed, as it should be installed in projects that use it instead.
- `racket_7_9` has been removed, as it is insecure. It is recommended to use Racket 8 instead.
- `fluxus` has been removed, as it depends on `racket_7_9` and had no updates in 9 years.
- The behavior of the `networking.nat.externalIP` and `networking.nat.externalIPv6` options has been changed. `networking.nat.forwardPorts` now only forwards packets destined for the specified IP addresses.
- `nodePackages.meshcommander` has been removed, as the package was deprecated by Intel.
- `kanata` was updated to v1.7.0, which introduces several breaking changes.
See the release notes of
[v1.7.0](https://github.com/jtroo/kanata/releases/tag/v1.7.0)
@@ -51,14 +70,20 @@
files have changed from `$out/share/fonts/{opentype,truetype}/NerdFonts` to
`$out/share/fonts/{opentype,truetype}/NerdFonts/<fontDirName>`, where `<fontDirName>` can be found in the
[official website](https://www.nerdfonts.com/font-downloads) as the titles in preview images, with the "Nerd Font"
suffix and any whitespaces trimmed.
suffix and any whitespaces trimmed. Configuration changes are required, see build output.
- `retroarch` has been refactored and the older `retroarch.override { cores = [ ... ]; }` to create a RetroArch derivation with custom cores doesn't work anymore, use `retroarch.withCores (cores: [ ... ])` instead. If you need more customization (e.g.: custom settings), use `wrapRetroArch` instead.
- `gkraken` software and `hardware.gkraken.enable` option have been removed, use `coolercontrol` via `programs.coolercontrol.enable` option instead.
- `nodePackages.ganache` has been removed, as the package has been deprecated by upstream.
- `containerd` has been updated to v2, which contains breaking changes. See the [containerd
2.0](https://github.com/containerd/containerd/blob/main/docs/containerd-2.0.md) documentation for more
details.
- `nodePackages.stackdriver-statsd-backend` has been removed, as the StackDriver service has been discontinued by Google, and therefore the package no longer works.
- the notmuch vim plugin now lives in a separate output of the `notmuch`
package. Installing `notmuch` will not bring the notmuch vim package anymore,
add `vimPlugins.notmuch-vim` to your (Neo)vim configuration if you want the
@@ -70,6 +95,10 @@
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
- Cinnamon has been updated to 6.4.
- `services.avahi.ipv6` now defaults to true.
- `bind.cacheNetworks` now only controls access for recursive queries, where it previously controlled access for all queries.
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
+1 -1
View File
@@ -473,7 +473,7 @@ let format' = format; in let
additionalSpace=$(( $(numfmt --from=iec '${additionalSpace}') + reservedSpace ))
# Compute required space in filesystem blocks
diskUsage=$(find . ! -type d -print0 | du --files0-from=- --apparent-size --block-size "${blockSize}" | cut -f1 | sum_lines)
diskUsage=$(find . ! -type d -print0 | du --files0-from=- --apparent-size --count-links --block-size "${blockSize}" | cut -f1 | sum_lines)
# Each inode takes space!
numInodes=$(find . | wc -l)
# Convert to bytes, inodes take two blocks each!
+2 -2
View File
@@ -30,14 +30,14 @@ rec {
guestSystem = pkgs.stdenv.hostPlatform.system;
linuxHostGuestMatrix = {
x86_64-linux = "${qemuPkg}/bin/qemu-kvm -cpu max";
x86_64-linux = "${qemuPkg}/bin/qemu-system-x86_64 -machine accel=kvm:tcg -cpu max";
armv7l-linux = "${qemuPkg}/bin/qemu-system-arm -machine virt,accel=kvm:tcg -cpu max";
aarch64-linux = "${qemuPkg}/bin/qemu-system-aarch64 -machine virt,gic-version=max,accel=kvm:tcg -cpu max";
powerpc64le-linux = "${qemuPkg}/bin/qemu-system-ppc64 -machine powernv";
powerpc64-linux = "${qemuPkg}/bin/qemu-system-ppc64 -machine powernv";
riscv32-linux = "${qemuPkg}/bin/qemu-system-riscv32 -machine virt";
riscv64-linux = "${qemuPkg}/bin/qemu-system-riscv64 -machine virt";
x86_64-darwin = "${qemuPkg}/bin/qemu-kvm -cpu max";
x86_64-darwin = "${qemuPkg}/bin/qemu-system-x86_64 -machine accel=kvm:tcg -cpu max";
};
otherHostGuestMatrix = {
aarch64-darwin = {
+19
View File
@@ -343,6 +343,25 @@ utils = rec {
in
filter (x: !(elem (getName x) namesToRemove)) packages;
/* Returns false if a package with the same name as the `package` is present in `packagesToDisable`.
Type:
disablePackageByName :: package -> [package] -> bool
Example:
disablePackageByName file-roller [ file-roller totem ]
=> false
Example:
disablePackageByName nautilus [ file-roller totem ]
=> true
*/
disablePackageByName = package: packagesToDisable:
let
namesToDisable = map getName packagesToDisable;
in
!elem (getName package) namesToDisable;
systemdUtils = {
lib = import ./systemd-lib.nix { inherit lib config pkgs utils; };
unitOptions = import ./systemd-unit-options.nix { inherit lib systemdUtils; };
+23 -11
View File
@@ -131,18 +131,30 @@ with lib;
boot.loader.timeout = 10;
boot.postBootCommands =
''
# After booting, register the contents of the Nix store
# in the Nix database in the tmpfs.
${config.nix.package}/bin/nix-store --load-db < /nix/store/nix-path-registration
boot.postBootCommands = ''
# After booting, register the contents of the Nix store
# in the Nix database in the tmpfs.
${config.nix.package}/bin/nix-store --load-db < /nix/store/nix-path-registration
# nixos-rebuild also requires a "system" profile and an
# /etc/NIXOS tag.
touch /etc/NIXOS
${config.nix.package}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
'';
# nixos-rebuild also requires a "system" profile and an
# /etc/NIXOS tag.
touch /etc/NIXOS
${config.nix.package}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
# Set password for user nixos if specified on cmdline
# Allows using nixos-anywhere in headless environments
for o in $(</proc/cmdline); do
case "$o" in
live.nixos.passwordHash=*)
set -- $(IFS==; echo $o)
${pkgs.gnugrep}/bin/grep -q "root::" /etc/shadow && ${pkgs.shadow}/bin/usermod -p "$2" root
;;
live.nixos.password=*)
set -- $(IFS==; echo $o)
${pkgs.gnugrep}/bin/grep -q "root::" /etc/shadow && echo "root:$2" | ${pkgs.shadow}/bin/chpasswd
;;
esac
done
'';
};
}
-11
View File
@@ -57,17 +57,6 @@ in
"nmi_watchdog=panic"
"softlockup_panic=1"
];
kernelPatches = [ {
name = "crashdump-config";
patch = null;
extraConfig = ''
CRASH_DUMP y
DEBUG_INFO y
PROC_VMCORE y
LOCKUP_DETECTOR y
HARDLOCKUP_DETECTOR y
'';
} ];
};
};
}
+15 -1
View File
@@ -278,11 +278,23 @@ in
PRUNE_BIND_MOUNTS = if cfg.pruneBindMounts then "yes" else "no";
};
serviceConfig = {
CapabilityBoundingSet = "CAP_DAC_READ_SEARCH CAP_CHOWN";
Nice = 19;
IOSchedulingClass = "idle";
IPAddressDeny = "any";
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateTmp = "yes";
PrivateDevices = true;
PrivateNetwork = "yes";
NoNewPrivileges = "yes";
ProtectClock = true;
ProtectControlGroups = true;
ProtectHostname = true;
RestrictAddressFamilies = "AF_UNIX";
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
ReadOnlyPaths = "/";
# Use dirOf cfg.output because mlocate creates temporary files next to
# the actual database. We could specify and create them as well,
@@ -290,6 +302,8 @@ in
# NOTE: If /var/cache does not exist, this leads to the misleading error message:
# update-locatedb.service: Failed at step NAMESPACE spawning …/update-locatedb-start: No such file or directory
ReadWritePaths = dirOf cfg.output;
SystemCallArchitectures = "native";
SystemCallFilter = "@system-service @chown";
};
};
+2
View File
@@ -588,6 +588,7 @@
./services/hardware/bluetooth.nix
./services/hardware/bolt.nix
./services/hardware/brltty.nix
./services/hardware/buffyboard.nix
./services/hardware/ddccontrol.nix
./services/hardware/display.nix
./services/hardware/fancontrol.nix
@@ -947,6 +948,7 @@
./services/monitoring/telegraf.nix
./services/monitoring/thanos.nix
./services/monitoring/todesk.nix
./services/monitoring/traccar.nix
./services/monitoring/tremor-rs.nix
./services/monitoring/tuptime.nix
./services/monitoring/unpoller.nix
+1 -1
View File
@@ -313,5 +313,5 @@ in
'';
};
meta.maintainers = with lib.maintainers; [ sigmasquadron ];
}
+2 -4
View File
@@ -1668,13 +1668,11 @@ in
(lib.concatMap lib.attrValues)
(lib.filter (rule: rule.enable))
(lib.catAttrs "modulePath")
# TODO(@uninsane): replace this warning + lib.filter with just an assertion
(map (modulePath: lib.warnIfNot
(map (modulePath: lib.throwIfNot
(lib.hasPrefix "/" modulePath)
''non-absolute PAM modulePath "${modulePath}" is unsupported by apparmor and will be treated as an error by future versions of nixpkgs; see <https://github.com/NixOS/nixpkgs/pull/314791>''
''non-absolute PAM modulePath "${modulePath}" is unsupported by apparmor''
modulePath
))
(lib.filter (lib.hasPrefix "/"))
lib.unique
(map (module: "mr ${module},"))
concatLines
+14 -11
View File
@@ -170,15 +170,6 @@ static int make_caps_ambient(const char *self_path) {
"MALLOC_ARENA_TEST\0"
int main(int argc, char **argv) {
ASSERT(argc >= 1);
// argv[0] goes into a lot of places, to a far greater degree than other elements
// of argv. glibc has had buffer overflows relating to argv[0], eg CVE-2023-6246.
// Since we expect the wrappers to be invoked from either $PATH or /run/wrappers/bin,
// there should be no reason to pass any particularly large values here, so we can
// be strict for strictness' sake.
ASSERT(strlen(argv[0]) < 512);
int debug = getenv(wrapper_debug) != NULL;
// Drop insecure environment variables explicitly
@@ -209,10 +200,22 @@ int main(int argc, char **argv) {
return 1;
}
char *replacement_argv[2] = {SOURCE_PROG, NULL};
char *old_argv0;
// Replace untrusted or missing argv[0] by the wrapped program path.
// This mitigates vulnerabilities caused by incorrect handling in privileged code.
if (argv[0]) {
old_argv0 = argv[0];
argv[0] = SOURCE_PROG;
} else {
old_argv0 = "«nullptr»";
argv = replacement_argv;
}
execve(SOURCE_PROG, argv, environ);
fprintf(stderr, "%s: cannot run `%s': %s\n",
argv[0], SOURCE_PROG, strerror(errno));
old_argv0, SOURCE_PROG, strerror(errno));
return 1;
}
@@ -52,7 +52,7 @@ with lib;
} // cfg.extraEnvironment;
path = (with pkgs; [
bash
bashInteractive
coreutils
git
gnutar
@@ -139,7 +139,7 @@ in
serviceConfig = {
ExecStart = lib.escapeShellArgs (
startCLIList
++ lib.optionals (cfg.prometheusConfig != null) [ "-promscrape.config=${prometheusConfigYml}" ]
++ lib.optionals (cfg.prometheusConfig != {}) [ "-promscrape.config=${prometheusConfigYml}" ]
);
DynamicUser = true;
@@ -90,6 +90,7 @@ in
lomiri-filemanager-app
lomiri-gallery-app
lomiri-history-service
lomiri-mediaplayer-app
lomiri-polkit-agent
lomiri-schemas # exposes some required dbus interfaces
lomiri-session # wrappers to properly launch the session
@@ -8,7 +8,6 @@ in {
options = {
services.espanso = {
enable = mkEnableOption "Espanso";
wayland = mkEnableOption "use the Wayland compatible espanso package";
package = mkPackageOption pkgs "espanso" {
example = "pkgs.espanso-wayland";
};
+1 -1
View File
@@ -88,7 +88,7 @@ in
"asusd/anime.ron" = maybeConfig "anime.ron" cfg.animeConfig;
"asusd/asusd.ron" = maybeConfig "asusd.ron" cfg.asusdConfig;
"asusd/aura.ron" = maybeConfig "aura.ron" cfg.auraConfig;
"asusd/profile.conf" = maybeConfig "profile.ron" cfg.profileConfig;
"asusd/profile.ron" = maybeConfig "profile.ron" cfg.profileConfig;
"asusd/fan_curves.ron" = maybeConfig "fan_curves.ron" cfg.fanCurvesConfig;
"asusd/asusd_user_ledmodes.ron" = maybeConfig "asusd_user_ledmodes.ron" cfg.userLedModesConfig;
};
@@ -0,0 +1,138 @@
# INTEGRATION NOTES:
# Buffyboard integrates as a virtual device in /dev/input
# which reads touch or pointer events from other input devices
# and generates events based on where those map to the keys it renders to the framebuffer.
#
# Buffyboard generates these events whether or not its onscreen keyboard is actually visible.
# Hence special care is needed if running anything which claims ownership of the display (such as a desktop environment),
# to avoid unwanted input events being triggered during normal desktop operation.
#
# Desktop users are recommended to either:
# 1. Stop buffyboard once your DE is started.
# e.g. `services.buffyboard.unitConfig.Conflicts = [ "my-de.service" ];`
# 2. Configure your DE to ignore input events from buffyboard (product-id=25209; vendor-id=26214; name=rd)
# e.g. `echo 'input "26214:25209:rd" events disabled' > ~/.config/sway/config`
{
config,
lib,
pkgs,
utils,
...
}:
let
cfg = config.services.buffyboard;
ini = pkgs.formats.ini { };
in
{
meta.maintainers = with lib.maintainers; [ colinsane ];
options = {
services.buffyboard = with lib; {
enable = mkEnableOption "buffyboard framebuffer keyboard (on-screen keyboard)";
package = mkPackageOption pkgs "buffybox" { };
extraFlags = mkOption {
type = types.listOf types.str;
default = [ ];
description = ''
Extra CLI arguments to pass to buffyboard.
'';
example = [
"--geometry=1920x1080@640,0"
"--dpi=192"
"--rotate=2"
"--verbose"
];
};
configFile = mkOption {
type = lib.types.path;
default = ini.generate "buffyboard.conf" (lib.filterAttrsRecursive (_: v: v != null) cfg.settings);
defaultText = lib.literalExpression ''ini.generate "buffyboard.conf" cfg.settings'';
description = ''
Path to an INI format configuration file to provide Buffyboard.
By default, this is generated from whatever you've set in `settings`.
If specified manually, then `settings` is ignored.
For an example config file see [here](https://gitlab.postmarketos.org/postmarketOS/buffybox/-/blob/master/buffyboard/buffyboard.conf)
'';
};
settings = mkOption {
description = ''
Settings to include in /etc/buffyboard.conf.
Every option here is strictly optional:
Buffyboard will use its own baked-in defaults for those options left unset.
'';
type = types.submodule {
freeformType = ini.type;
options.input.pointer = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Enable or disable the use of a hardware mouse or other pointing device.
'';
};
options.input.touchscreen = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Enable or disable the use of the touchscreen.
'';
};
options.theme.default = mkOption {
type = types.either types.str (
types.enum [
null
"adwaita-dark"
"breezy-dark"
"breezy-light"
"nord-dark"
"nord-light"
"pmos-dark"
"pmos-light"
]
);
default = null;
description = ''
Selects the default theme on boot. Can be changed at runtime to the alternative theme.
'';
};
options.quirks.fbdev_force_refresh = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
If true and using the framebuffer backend, this triggers a display refresh after every draw operation.
This has a negative performance impact.
'';
};
};
default = { };
};
};
};
config = lib.mkIf cfg.enable {
systemd.packages = [ cfg.package ];
systemd.services.buffyboard = {
# upstream provides the service (including systemd hardening): we just configure it to start by default
# and override ExecStart so as to optionally pass extra arguments
serviceConfig.ExecStart = [
"" # clear default ExecStart
(utils.escapeSystemdExecArgs (
[
(lib.getExe' cfg.package "buffyboard")
"--config-override"
cfg.configFile
]
++ cfg.extraFlags
))
];
wantedBy = [ "getty.target" ];
before = [ "getty.target" ];
};
};
}
+2 -1
View File
@@ -853,7 +853,8 @@ in
smtpd_tls_cert_file = cfg.sslCert;
smtpd_tls_key_file = cfg.sslKey;
smtpd_tls_security_level = "may";
smtpd_tls_security_level = lib.mkDefault "may";
};
services.postfix.masterConfig = {
+1 -3
View File
@@ -228,7 +228,6 @@ in
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProcSubset = "pid";
RestrictAddressFamilies = [
"AF_UNIX"
@@ -240,11 +239,10 @@ in
RestrictRealtime = true;
LockPersonality = true;
MemoryDenyWriteExecute = true;
SystemCallFilter = [
"@sandbox"
"@system-service"
"~@privileged"
];
SystemCallArchitectures = "native";
+1 -1
View File
@@ -27,7 +27,7 @@ in
description = ''
Set the Access-Control-Allow-Origin header in the HTTP response,
used for direct (non-proxy) JavaScript-based access from browsers.
`null` to allow access from all sites.
`"*"` to allow access from all sites.
'';
};
+2 -2
View File
@@ -69,9 +69,9 @@ in
serviceConfig = with pkgs; {
DynamicUser = true;
StateDirectory = baseNameOf libDir;
ExecStartPre = "${getBin pykms}/libexec/create_pykms_db.sh ${libDir}/clients.db";
ExecStartPre = "${lib.getBin pykms}/libexec/create_pykms_db.sh ${libDir}/clients.db";
ExecStart = lib.concatStringsSep " " ([
"${getBin pykms}/bin/server"
"${lib.getBin pykms}/bin/server"
"--logfile=STDOUT"
"--loglevel=${cfg.logLevel}"
"--sqlite=${libDir}/clients.db"
+1
View File
@@ -442,6 +442,7 @@ in
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateMounts = true;
PrivateTmp = true;
ProcSubset = "pid";
ProtectClock = true;
@@ -52,11 +52,13 @@ let
"keylight"
"klipper"
"knot"
"libvirt"
"lnd"
"mail"
"mikrotik"
"modemmanager"
"mongodb"
"mqtt"
"mysqld"
"nats"
"nextcloud"
@@ -332,6 +334,13 @@ in
Config file specified in `services.prometheus.exporters.ipmi.webConfigFile' must
not reside within /tmp - it won't be visible to the systemd service.
'';
} {
assertion =
cfg.restic.enable -> ((cfg.restic.repository == null) != (cfg.restic.repositoryFile == null));
message = ''
Please specify either 'services.prometheus.exporters.restic.repository'
or 'services.prometheus.exporters.restic.repositoryFile'.
'';
} {
assertion = cfg.snmp.enable -> (
(cfg.snmp.configurationPath == null) != (cfg.snmp.configuration == null)
@@ -0,0 +1,29 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.prometheus.exporters.libvirt;
in
{
port = 9177;
extraOpts = {
libvirtUri = lib.mkOption {
type = lib.types.str;
default = "qemu:///system";
description = "Libvirt URI from which to extract metrics";
};
};
serviceOpts = {
serviceConfig = {
ExecStart = ''
${lib.getExe pkgs.prometheus-libvirt-exporter} \
--web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
--libvirt.uri ${cfg.libvirtUri} ${lib.concatStringsSep " " cfg.extraFlags}
'';
};
};
}
@@ -0,0 +1,140 @@
{
config,
lib,
pkgs,
options,
utils,
}:
let
inherit (lib)
mkIf
mkEnableOption
mkOption
types
;
cfg = config.services.prometheus.exporters.mqtt;
toConfigBoolean = x: if x then "True" else "False";
toConfigList = builtins.concatStringsSep ",";
in
{
# https://github.com/kpetremann/mqtt-exporter/tree/master?tab=readme-ov-file#configuration
port = 9000;
extraOpts = {
keepFullTopic = mkEnableOption "Keep entire topic instead of the first two elements only. Usecase: Shelly 3EM";
logLevel = mkOption {
type = types.enum [
"CRITICAL"
"ERROR"
"WARNING"
"INFO"
"DEBUG"
];
default = "INFO";
example = "DEBUG";
description = "Logging level";
};
logMqttMessage = mkEnableOption "Log MQTT original message, only if `LOG_LEVEL` is set to DEBUG.";
mqttIgnoredTopics = mkOption {
type = types.listOf types.str;
default = [ ];
description = "Lists of topics to ignore. Accepts wildcards.";
};
mqttAddress = mkOption {
type = types.str;
default = "127.0.0.1";
description = "IP or hostname of MQTT broker.";
};
mqttPort = mkOption {
type = types.port;
default = 1883;
description = "TCP port of MQTT broker.";
};
mqttTopic = mkOption {
type = types.str;
default = "#";
description = "Topic path to subscribe to.";
};
mqttKeepAlive = mkOption {
type = types.int;
default = 60;
example = 30;
description = "Keep alive interval to maintain connection with MQTT broker.";
};
mqttUsername = mkOption {
type = types.nullOr types.str;
default = null;
example = "mqttexporter";
description = "Username which should be used to authenticate against the MQTT broker.";
};
mqttV5Protocol = mkEnableOption "Force to use MQTT protocol v5 instead of 3.1.1.";
mqttClientId = mkOption {
type = types.nullOr types.str;
default = null;
description = "Set client ID manually for MQTT connection";
};
mqttExposeClientId = mkEnableOption "Expose the client ID as a label in Prometheus metrics.";
prometheusPrefix = mkOption {
type = types.str;
default = "mqtt_";
description = "Prefix added to the metric name.";
};
topicLabel = mkOption {
type = types.str;
default = "topic";
description = "Define the Prometheus label for the topic.";
};
zigbee2MqttAvailability = mkEnableOption "Normalize sensor name for device availability metric added by Zigbee2MQTT.";
zwaveTopicPrefix = mkOption {
type = types.str;
default = "zwave/";
description = "MQTT topic used for Zwavejs2Mqtt messages.";
};
esphomeTopicPrefixes = mkOption {
type = types.listOf types.str;
default = [ ];
description = "MQTT topic used for ESPHome messages.";
};
hubitatTopicPrefixes = mkOption {
type = types.listOf types.str;
default = [ "hubitat/" ];
description = "MQTT topic used for Hubitat messages.";
};
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
example = [ "/run/secrets/mqtt-exporter" ];
description = ''
File to load as environment file. Useful for e.g. setting `MQTT_PASSWORD`
without putting any secrets into the Nix store.
'';
};
};
serviceOpts = {
environment = {
KEEP_FULL_TOPIC = toConfigBoolean cfg.keepFullTopic;
LOG_LEVEL = cfg.logLevel;
LOG_MQTT_MESSAGE = toConfigBoolean cfg.logMqttMessage;
MQTT_IGNORED_TOPIC = toConfigList cfg.mqttIgnoredTopics;
MQTT_ADDRESS = cfg.mqttAddress;
MQTT_PORT = toString cfg.mqttPort;
MQTT_TOPIC = cfg.mqttTopic;
MQTT_KEEPALIVE = toString cfg.mqttKeepAlive;
MQTT_USERNAME = cfg.mqttUsername;
MQTT_V5_PROTOCOL = toConfigBoolean cfg.mqttV5Protocol;
MQTT_CLIENT_ID = mkIf (cfg.mqttClientId != null) cfg.mqttClientId;
PROMETHEUS_ADDRESS = cfg.listenAddress;
PROMETHEUS_PORT = toString cfg.port;
PROMETHEUS_PREFIX = cfg.prometheusPrefix;
TOPIC_LABEL = cfg.topicLabel;
ZIGBEE2MQTT_AVAILABILITY = toConfigBoolean cfg.zigbee2MqttAvailability;
ZWAVE_TOPIC_PREFIX = cfg.zwaveTopicPrefix;
ESPHOME_TOPIC_PREFIXES = toConfigList cfg.esphomeTopicPrefixes;
HUBITAT_TOPIC_PREFIXES = toConfigList cfg.hubitatTopicPrefixes;
};
serviceConfig = {
EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile;
ExecStart = lib.getExe pkgs.mqtt-exporter;
};
};
}
@@ -10,6 +10,7 @@ let
mapAttrs'
splitString
toUpper
optional
optionalAttrs
nameValuePair
;
@@ -18,13 +19,22 @@ in
port = 9753;
extraOpts = {
repository = mkOption {
type = types.str;
type = with lib.types; nullOr str;
default = null;
description = ''
URI pointing to the repository to monitor.
'';
example = "sftp:backup@192.168.1.100:/backups/example";
};
repositoryFile = mkOption {
type = with lib.types; nullOr path;
default = null;
description = ''
Path to the file containing the URI for the repository to monitor.
'';
};
passwordFile = mkOption {
type = types.path;
description = ''
@@ -103,13 +113,21 @@ in
serviceOpts = {
script = ''
export RESTIC_REPOSITORY=${
if cfg.repositoryFile != null
then "$(cat $CREDENTIALS_DIRECTORY/RESTIC_REPOSITORY)"
else "${cfg.repository}"
}
export RESTIC_PASSWORD_FILE=$CREDENTIALS_DIRECTORY/RESTIC_PASSWORD_FILE
${pkgs.prometheus-restic-exporter}/bin/restic-exporter.py \
${concatStringsSep " \\\n " cfg.extraFlags}
'';
serviceConfig = {
EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile;
LoadCredential = [ "RESTIC_PASSWORD_FILE:${cfg.passwordFile}" ];
LoadCredential =
[ "RESTIC_PASSWORD_FILE:${cfg.passwordFile}" ]
++ optional (cfg.repositoryFile != null)
[ "RESTIC_REPOSITORY:${cfg.repositoryFile}" ];
};
environment =
let
@@ -119,7 +137,6 @@ in
toRcloneVal = v: if lib.isBool v then lib.boolToString v else v;
in
{
RESTIC_REPOSITORY = cfg.repository;
LISTEN_ADDRESS = cfg.listenAddress;
LISTEN_PORT = toString cfg.port;
REFRESH_INTERVAL = toString cfg.refreshInterval;
@@ -87,7 +87,6 @@ in
"console"
"repeater"
"statsd-librato-backend"
"stackdriver-statsd-backend"
"statsd-influxdb-backend"
];
type = lib.types.listOf lib.types.str;
@@ -0,0 +1,125 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.traccar;
stateDirectory = "/var/lib/traccar";
configFilePath = "${stateDirectory}/config.xml";
expandCamelCase = lib.replaceStrings lib.upperChars (map (s: ".${s}") lib.lowerChars);
mkConfigEntry = key: value: "<entry key='${expandCamelCase key}'>${value}</entry>";
mkConfig =
configurationOptions:
pkgs.writeText "traccar.xml" ''
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE properties SYSTEM 'http://java.sun.com/dtd/properties.dtd'>
<properties>
${builtins.concatStringsSep "\n" (lib.mapAttrsToList mkConfigEntry configurationOptions)}
</properties>
'';
defaultConfig = {
databaseDriver = "org.h2.Driver";
databasePassword = "";
databaseUrl = "jdbc:h2:${stateDirectory}/traccar";
databaseUser = "sa";
loggerConsole = "true";
mediaPath = "${stateDirectory}/media";
templatesRoot = "${stateDirectory}/templates";
};
in
{
options.services.traccar = {
enable = lib.mkEnableOption "Traccar, an open source GPS tracking system";
settings = lib.mkOption {
apply = lib.recursiveUpdate defaultConfig;
default = defaultConfig;
description = ''
{file}`config.xml` configuration as a Nix attribute set.
Attribute names are translated from camelCase to dot-separated strings. For instance:
{option}`mailSmtpPort = "25"`
would result in the following configuration property:
`<entry key='mail.smtp.port'>25</entry>`
Configuration options should match those described in
[Traccar - Configuration File](https://www.traccar.org/configuration-file/).
Secret tokens should be specified using {option}`environmentFile`
instead of this world-readable attribute set.
'';
};
environmentFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = ''
File containing environment variables to substitute in the configuration before starting Traccar.
Can be used for storing the secrets without making them available in the world-readable Nix store.
For example, you can set {option}`services.traccar.settings.databasePassword = "$TRACCAR_DB_PASSWORD"`
and then specify `TRACCAR_DB_PASSWORD="<secret>"` in the environment file.
This value will get substituted in the configuration file.
'';
};
};
config =
let
configuration = mkConfig cfg.settings;
in
lib.mkIf cfg.enable {
systemd.services.traccar = {
enable = true;
description = "Traccar";
after = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
preStart = ''
# Copy new templates into our state directory.
cp -a --update=none ${pkgs.traccar}/templates ${stateDirectory}
test -f '${configFilePath}' && rm -f '${configFilePath}'
# Substitute the configFile from Envvars read from EnvironmentFile
old_umask=$(umask)
umask 0177
${lib.getExe pkgs.envsubst} \
-i ${configuration} \
-o ${configFilePath}
umask $old_umask
'';
serviceConfig = {
DynamicUser = true;
EnvironmentFile = cfg.environmentFile;
ExecStart = "${lib.getExe pkgs.traccar} ${configFilePath}";
LockPersonality = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateTmp = true;
PrivateUsers = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectSystem = "strict";
Restart = "on-failure";
RestartSec = 10;
RestrictRealtime = true;
RestrictSUIDSGID = true;
StateDirectory = "traccar";
SuccessExitStatus = 143;
Type = "simple";
# Set the working directory to traccar's package.
# Traccar only searches for the DB migrations relative to it's WorkingDirectory and nothing worked to
# work around this. To avoid copying the migrations over to the state directory, we use the package as
# WorkingDirectory.
WorkingDirectory = "${pkgs.traccar}";
};
};
};
}
@@ -51,21 +51,30 @@ in
DynamicUser = true;
ExecStart = "${cfg.package}/bin/uptime-kuma-server";
Restart = "on-failure";
ProtectHome = true;
ProtectSystem = "strict";
PrivateTmp = true;
PrivateDevices = true;
ProtectHostname = true;
ProtectClock = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
AmbientCapabilities = "";
CapabilityBoundingSet = "";
LockPersonality = true;
MemoryDenyWriteExecute = false; # enabling it breaks execution
NoNewPrivileges = true;
PrivateDevices = true;
PrivateMounts = true;
PrivateTmp = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "noaccess";
ProtectSystem = "strict";
RemoveIPC = true;
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" "AF_NETLINK" ];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
RemoveIPC = true;
PrivateMounts = true;
SystemCallArchitectures = "native";
UMask = 027;
};
};
};
@@ -115,7 +115,7 @@ in {
CacheDirectory = "vmagent";
ExecStart = lib.escapeShellArgs (
startCLIList
++ lib.optionals (cfg.prometheusConfig != null) ["-promscrape.config=${prometheusConfigYml}"]
++ lib.optionals (cfg.prometheusConfig != {}) ["-promscrape.config=${prometheusConfigYml}"]
);
LoadCredential = lib.optional (cfg.remoteWrite.basicAuthPasswordFile != null) [
"remote_write_basic_auth_password:${cfg.remoteWrite.basicAuthPasswordFile}"
@@ -101,6 +101,19 @@ in
};
};
usershares = {
enable = lib.mkEnableOption "user-configurable Samba shares";
group = lib.mkOption {
type = lib.types.str;
default = "samba";
description = ''
Name of the group members of which will be allowed to create usershares.
The group will be created automatically.
'';
};
};
nsswins = lib.mkEnableOption ''
WINS NSS (Name Service Switch) plug-in.
@@ -308,5 +321,22 @@ in
restartTriggers = [ configFile ];
};
})
(lib.mkIf (cfg.enable && cfg.usershares.enable) {
users.groups.${cfg.usershares.group} = {};
systemd.tmpfiles.settings."50-samba-usershares"."/var/lib/samba/usershares".d = {
user = "root";
group = cfg.usershares.group;
mode = "1775"; # sticky so users can't delete others' shares
};
# set some reasonable defaults
services.samba.settings.global = lib.mkDefault {
"usershare path" = "/var/lib/samba/usershares";
"usershare max shares" = 100; # high enough to be considered ~unlimited
"usershare allow guests" = true;
};
})
];
}
@@ -91,7 +91,7 @@ in
ipv6 = lib.mkOption {
type = lib.types.bool;
default = false;
default = true;
description = "Whether to use IPv6.";
};
@@ -300,7 +300,7 @@ in {
capabilities = mkOption {
type = types.listOf types.str;
default = ["HT40" "HT40-" "SHORT-GI-20" "SHORT-GI-40"];
default = ["HT40" "SHORT-GI-20" "SHORT-GI-40"];
example = ["LDPC" "HT40+" "HT40-" "GF" "SHORT-GI-20" "SHORT-GI-40" "TX-STBC" "RX-STBC1"];
description = ''
HT (High Throughput) capabilities given as a list of flags.
@@ -23,7 +23,7 @@ in with lib; {
package = mkPackageOption pkgs "zeronet" { };
settings = mkOption {
type = with types; attrsOf (oneOf [ str int bool (listOf str) ]);
type = with types; attrsOf (attrsOf (oneOf [ str int bool (listOf str) ]));
default = {};
example = literalExpression "{ global.tor = enable; }";
+5 -3
View File
@@ -4,7 +4,7 @@ with lib;
let
inherit (pkgs) cups-pk-helper cups-filters xdg-utils;
inherit (pkgs) cups-pk-helper libcupsfilters cups-filters xdg-utils;
cfg = config.services.printing;
cups = cfg.package;
@@ -35,7 +35,7 @@ let
bindir = pkgs.buildEnv {
name = "cups-progs";
paths =
[ cups.out additionalBackends cups-filters pkgs.ghostscript ]
[ cups.out additionalBackends libcupsfilters cups-filters pkgs.ghostscript ]
++ cfg.drivers;
pathsToLink = [ "/lib" "/share/cups" "/bin" ];
postBuild = cfg.bindirCmds;
@@ -278,6 +278,8 @@ in
'';
};
browsed.package = lib.mkPackageOption pkgs "cups-browsed" {};
browsedConf = mkOption {
type = types.lines;
default = "";
@@ -436,7 +438,7 @@ in
path = [ cups ];
serviceConfig.ExecStart = "${cups-filters}/bin/cups-browsed";
serviceConfig.ExecStart = "${cfg.browsed.package}/bin/cups-browsed";
restartTriggers = [ browsedFile ];
};
+1 -1
View File
@@ -21,7 +21,7 @@ let
in
{
imports = [
(mkRemovedOptionModule [ "debug" ] ''
(mkRemovedOptionModule [ "services" "aesmd" "debug" ] ''
Enable debug mode by overriding the aesmd package directly:
services.aesmd.package = pkgs.sgx-psw.override { debug = true; };
+14 -1
View File
@@ -231,7 +231,10 @@ in
enableServer = mkEnableOption "the Kanidm server";
enablePam = mkEnableOption "the Kanidm PAM and NSS integration";
package = mkPackageOption pkgs "kanidm" { };
package = mkPackageOption pkgs "kanidm" {
example = "kanidm_1_4";
extraDescription = "If not set will receive a specific version based on stateVersion. Set to `pkgs.kanidm` to always receive the latest version, with the understanding that this could introduce breaking changes.";
};
serverSettings = mkOption {
type = types.submodule {
@@ -811,6 +814,16 @@ in
)
);
services.kanidm.package =
let
pkg =
if lib.versionAtLeast config.system.stateVersion "24.11" then
pkgs.kanidm_1_4
else
lib.warn "No default kanidm package found for stateVersion = '${config.system.stateVersion}'. Using unpinned version. Consider setting `services.kanidm.package = pkgs.kanidm_1_x` to avoid upgrades introducing breaking changes." pkgs.kanidm;
in
lib.mkDefault pkg;
environment.systemPackages = mkIf cfg.enableClient [ cfg.package ];
systemd.tmpfiles.settings."10-kanidm" = {
@@ -46,8 +46,8 @@ let
};
template = mkOption {
default = [ ];
type = with types; listOf (attrsOf anything);
default = null;
type = with types; nullOr (listOf (attrsOf anything));
description =
let upstreamDocs =
if flavour == "vault-agent"
+2
View File
@@ -134,6 +134,8 @@ in
Group = cfg.group;
RemoveIPC = true;
PrivateTmp = true;
# https://github.com/twosigma/nsncd/pull/33/files#r1496927653
Environment = [ "NSNCD_HANDOFF_TIMEOUT=10" ];
NoNewPrivileges = true;
RestrictSUIDSGID = true;
ProtectSystem = "strict";
+284 -195
View File
@@ -1,8 +1,29 @@
{ config, lib, pkgs, options, ... }:
with lib;
{
config,
lib,
pkgs,
options,
...
}:
let
inherit (lib)
mkRenamedOptionModule
mkAliasOptionModuleMD
mkEnableOption
mkOption
types
literalExpression
mkPackageOption
mkIf
optionalString
optional
mkDefault
escapeShellArgs
optionalAttrs
mkMerge
;
cfg = config.services.transmission;
opt = options.services.transmission;
inherit (config.environment) etc;
@@ -12,15 +33,36 @@ let
downloadsDir = "Downloads";
incompleteDir = ".incomplete";
watchDir = "watchdir";
settingsFormat = pkgs.formats.json {};
settingsFormat = pkgs.formats.json { };
settingsFile = settingsFormat.generate "settings.json" cfg.settings;
in
{
imports = [
(mkRenamedOptionModule ["services" "transmission" "port"]
["services" "transmission" "settings" "rpc-port"])
(mkAliasOptionModuleMD ["services" "transmission" "openFirewall"]
["services" "transmission" "openPeerPorts"])
(mkRenamedOptionModule
[
"services"
"transmission"
"port"
]
[
"services"
"transmission"
"settings"
"rpc-port"
]
)
(mkAliasOptionModuleMD
[
"services"
"transmission"
"openFirewall"
]
[
"services"
"transmission"
"openPeerPorts"
]
)
];
options = {
services.transmission = {
@@ -46,130 +88,137 @@ in
See [Transmission's Wiki](https://github.com/transmission/transmission/wiki/Editing-Configuration-Files)
for documentation of settings not explicitly covered by this module.
'';
default = {};
default = { };
type = types.submodule {
freeformType = settingsFormat.type;
options.download-dir = mkOption {
type = types.path;
default = "${cfg.home}/${downloadsDir}";
defaultText = literalExpression ''"''${config.${opt.home}}/${downloadsDir}"'';
description = "Directory where to download torrents.";
};
options.incomplete-dir = mkOption {
type = types.path;
default = "${cfg.home}/${incompleteDir}";
defaultText = literalExpression ''"''${config.${opt.home}}/${incompleteDir}"'';
description = ''
When enabled with
services.transmission.home
[](#opt-services.transmission.settings.incomplete-dir-enabled),
new torrents will download the files to this directory.
When complete, the files will be moved to download-dir
[](#opt-services.transmission.settings.download-dir).
'';
};
options.incomplete-dir-enabled = mkOption {
type = types.bool;
default = true;
description = "";
};
options.message-level = mkOption {
type = types.ints.between 0 6;
default = 2;
description = "Set verbosity of transmission messages.";
};
options.peer-port = mkOption {
type = types.port;
default = 51413;
description = "The peer port to listen for incoming connections.";
};
options.peer-port-random-high = mkOption {
type = types.port;
default = 65535;
description = ''
The maximum peer port to listen to for incoming connections
when [](#opt-services.transmission.settings.peer-port-random-on-start) is enabled.
'';
};
options.peer-port-random-low = mkOption {
type = types.port;
default = 65535;
description = ''
The minimal peer port to listen to for incoming connections
when [](#opt-services.transmission.settings.peer-port-random-on-start) is enabled.
'';
};
options.peer-port-random-on-start = mkOption {
type = types.bool;
default = false;
description = "Randomize the peer port.";
};
options.rpc-bind-address = mkOption {
type = types.str;
default = "127.0.0.1";
example = "0.0.0.0";
description = ''
Where to listen for RPC connections.
Use `0.0.0.0` to listen on all interfaces.
'';
};
options.rpc-port = mkOption {
type = types.port;
default = 9091;
description = "The RPC port to listen to.";
};
options.script-torrent-done-enabled = mkOption {
type = types.bool;
default = false;
description = ''
Whether to run
[](#opt-services.transmission.settings.script-torrent-done-filename)
at torrent completion.
'';
};
options.script-torrent-done-filename = mkOption {
type = types.nullOr types.path;
default = null;
description = "Executable to be run at torrent completion.";
};
options.umask = mkOption {
type = types.int;
default = 2;
description = ''
Sets transmission's file mode creation mask.
See the umask(2) manpage for more information.
Users who want their saved torrents to be world-writable
may want to set this value to 0.
Bear in mind that the json markup language only accepts numbers in base 10,
so the standard umask(2) octal notation "022" is written in settings.json as 18.
'';
};
options.utp-enabled = mkOption {
type = types.bool;
default = true;
description = ''
Whether to enable [Micro Transport Protocol (µTP)](https://en.wikipedia.org/wiki/Micro_Transport_Protocol).
'';
};
options.watch-dir = mkOption {
type = types.path;
default = "${cfg.home}/${watchDir}";
defaultText = literalExpression ''"''${config.${opt.home}}/${watchDir}"'';
description = "Watch a directory for torrent files and add them to transmission.";
};
options.watch-dir-enabled = mkOption {
type = types.bool;
default = false;
description = ''Whether to enable the
[](#opt-services.transmission.settings.watch-dir).
'';
};
options.trash-original-torrent-files = mkOption {
type = types.bool;
default = false;
description = ''Whether to delete torrents added from the
[](#opt-services.transmission.settings.watch-dir).
'';
options = {
download-dir = mkOption {
type = types.path;
default = "${cfg.home}/${downloadsDir}";
defaultText = literalExpression ''"''${config.${opt.home}}/${downloadsDir}"'';
description = "Directory where to download torrents.";
};
incomplete-dir = mkOption {
type = types.path;
default = "${cfg.home}/${incompleteDir}";
defaultText = literalExpression ''"''${config.${opt.home}}/${incompleteDir}"'';
description = ''
When enabled with
services.transmission.home
[](#opt-services.transmission.settings.incomplete-dir-enabled),
new torrents will download the files to this directory.
When complete, the files will be moved to download-dir
[](#opt-services.transmission.settings.download-dir).
'';
};
incomplete-dir-enabled = mkOption {
type = types.bool;
default = true;
description = "";
};
message-level = mkOption {
type = types.ints.between 0 6;
default = 2;
description = "Set verbosity of transmission messages.";
};
peer-port = mkOption {
type = types.port;
default = 51413;
description = "The peer port to listen for incoming connections.";
};
peer-port-random-high = mkOption {
type = types.port;
default = 65535;
description = ''
The maximum peer port to listen to for incoming connections
when [](#opt-services.transmission.settings.peer-port-random-on-start) is enabled.
'';
};
peer-port-random-low = mkOption {
type = types.port;
default = 65535;
description = ''
The minimal peer port to listen to for incoming connections
when [](#opt-services.transmission.settings.peer-port-random-on-start) is enabled.
'';
};
peer-port-random-on-start = mkOption {
type = types.bool;
default = false;
description = "Randomize the peer port.";
};
rpc-bind-address = mkOption {
type = types.str;
default = "127.0.0.1";
example = "0.0.0.0";
description = ''
Where to listen for RPC connections.
Use `0.0.0.0` to listen on all interfaces.
'';
};
rpc-port = mkOption {
type = types.port;
default = 9091;
description = "The RPC port to listen to.";
};
script-torrent-done-enabled = mkOption {
type = types.bool;
default = false;
description = ''
Whether to run
[](#opt-services.transmission.settings.script-torrent-done-filename)
at torrent completion.
'';
};
script-torrent-done-filename = mkOption {
type = types.nullOr types.path;
default = null;
description = "Executable to be run at torrent completion.";
};
umask = mkOption {
type = types.either types.int types.str;
default = if cfg.package == pkgs.transmission_3 then 18 else "022";
defaultText = literalExpression "if cfg.package == pkgs.transmission_3 then 18 else \"022\"";
description = ''
Sets transmission's file mode creation mask.
See the umask(2) manpage for more information.
Users who want their saved torrents to be world-writable
may want to set this value to 0/`"000"`.
Keep in mind, that if you are using Transmission 3, this has to
be passed as a base 10 integer, whereas Transmission 4 takes
an octal number in a string instead.
'';
};
utp-enabled = mkOption {
type = types.bool;
default = true;
description = ''
Whether to enable [Micro Transport Protocol (µTP)](https://en.wikipedia.org/wiki/Micro_Transport_Protocol).
'';
};
watch-dir = mkOption {
type = types.path;
default = "${cfg.home}/${watchDir}";
defaultText = literalExpression ''"''${config.${opt.home}}/${watchDir}"'';
description = "Watch a directory for torrent files and add them to transmission.";
};
watch-dir-enabled = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable the
[](#opt-services.transmission.settings.watch-dir).
'';
};
trash-original-torrent-files = mkOption {
type = types.bool;
default = false;
description = ''
Whether to delete torrents added from the
[](#opt-services.transmission.settings.watch-dir).
'';
};
};
};
};
@@ -191,6 +240,17 @@ in
and [](#opt-services.transmission.settings.watch-dir).
Note that you may also want to change
[](#opt-services.transmission.settings.umask).
Keep in mind, that if the default user is used, the `home` directory
is locked behind a `750` permission, which affects all subdirectories
as well. There are 3 ways to get around this:
1. (Recommended) add the users that should have access to the group
set by [](#opt-services.transmission.group)
2. Change [](#opt-services.transmission.settings.download-dir) to be
under a directory that has the right permissions
3. Change `systemd.services.transmission.serviceConfig.StateDirectoryMode`
to the same value as this option
'';
};
@@ -231,7 +291,7 @@ in
extraFlags = mkOption {
type = types.listOf types.str;
default = [];
default = [ ];
example = [ "--log-debug" ];
description = ''
Extra flags passed to the transmission command in the service definition.
@@ -279,40 +339,51 @@ in
# when /home/foo is not owned by cfg.user.
# Note also that using an ExecStartPre= wouldn't work either
# because BindPaths= needs these directories before.
system.activationScripts = mkIf (cfg.downloadDirPermissions != null)
{ transmission-daemon = ''
install -d -m 700 '${cfg.home}/${settingsDir}'
chown -R '${cfg.user}:${cfg.group}' ${cfg.home}/${settingsDir}
system.activationScripts.transmission-daemon =
''
install -d -m 700 -o '${cfg.user}' -g '${cfg.group}' '${cfg.home}/${settingsDir}'
''
+ optionalString (cfg.downloadDirPermissions != null) ''
install -d -m '${cfg.downloadDirPermissions}' -o '${cfg.user}' -g '${cfg.group}' '${cfg.settings.download-dir}'
'' + optionalString cfg.settings.incomplete-dir-enabled ''
install -d -m '${cfg.downloadDirPermissions}' -o '${cfg.user}' -g '${cfg.group}' '${cfg.settings.incomplete-dir}'
'' + optionalString cfg.settings.watch-dir-enabled ''
install -d -m '${cfg.downloadDirPermissions}' -o '${cfg.user}' -g '${cfg.group}' '${cfg.settings.watch-dir}'
'';
};
${optionalString cfg.settings.incomplete-dir-enabled ''
install -d -m '${cfg.downloadDirPermissions}' -o '${cfg.user}' -g '${cfg.group}' '${cfg.settings.incomplete-dir}'
''}
${optionalString cfg.settings.watch-dir-enabled ''
install -d -m '${cfg.downloadDirPermissions}' -o '${cfg.user}' -g '${cfg.group}' '${cfg.settings.watch-dir}'
''}
'';
systemd.services.transmission = {
description = "Transmission BitTorrent Service";
after = [ "network.target" ] ++ optional apparmor.enable "apparmor.service";
requires = optional apparmor.enable "apparmor.service";
wantedBy = [ "multi-user.target" ];
environment.CURL_CA_BUNDLE = etc."ssl/certs/ca-certificates.crt".source;
environment.TRANSMISSION_WEB_HOME = lib.mkIf (cfg.webHome != null) cfg.webHome;
environment = {
CURL_CA_BUNDLE = etc."ssl/certs/ca-certificates.crt".source;
TRANSMISSION_WEB_HOME = lib.mkIf (cfg.webHome != null) cfg.webHome;
};
serviceConfig = {
# Use "+" because credentialsFile may not be accessible to User= or Group=.
ExecStartPre = [("+" + pkgs.writeShellScript "transmission-prestart" ''
set -eu${lib.optionalString (cfg.settings.message-level >= 3) "x"}
${pkgs.jq}/bin/jq --slurp add ${settingsFile} '${cfg.credentialsFile}' |
install -D -m 600 -o '${cfg.user}' -g '${cfg.group}' /dev/stdin \
'${cfg.home}/${settingsDir}/settings.json'
'')];
ExecStart="${cfg.package}/bin/transmission-daemon -f -g ${cfg.home}/${settingsDir} ${escapeShellArgs cfg.extraFlags}";
ExecStartPre = [
(
"+"
+ pkgs.writeShellScript "transmission-prestart" ''
set -eu${lib.optionalString (cfg.settings.message-level >= 3) "x"}
${pkgs.jq}/bin/jq --slurp add ${settingsFile} '${cfg.credentialsFile}' |
install -D -m 600 -o '${cfg.user}' -g '${cfg.group}' /dev/stdin \
'${cfg.home}/${settingsDir}/settings.json'
''
)
];
ExecStart = "${cfg.package}/bin/transmission-daemon -f -g ${cfg.home}/${settingsDir} ${escapeShellArgs cfg.extraFlags}";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
User = cfg.user;
Group = cfg.group;
# Create rootDir in the host's mount namespace.
RuntimeDirectory = [(baseNameOf rootDir)];
RuntimeDirectory = [ (baseNameOf rootDir) ];
RuntimeDirectoryMode = "755";
# This is for BindPaths= and BindReadOnlyPaths=
# to allow traversal of directories they create in RootDirectory=.
@@ -330,27 +401,30 @@ in
RootDirectoryStartOnly = true;
MountAPIVFS = true;
BindPaths =
[ "${cfg.home}/${settingsDir}"
[
"${cfg.home}/${settingsDir}"
cfg.settings.download-dir
# Transmission may need to read in the host's /run (eg. /run/systemd/resolve)
# or write in its private /run (eg. /run/host).
"/run"
] ++
optional cfg.settings.incomplete-dir-enabled
cfg.settings.incomplete-dir ++
optional (cfg.settings.watch-dir-enabled && cfg.settings.trash-original-torrent-files)
cfg.settings.watch-dir;
BindReadOnlyPaths = [
# No confinement done of /nix/store here like in systemd-confinement.nix,
# an AppArmor profile is provided to get a confinement based upon paths and rights.
builtins.storeDir
"/etc"
] ++
optional (cfg.settings.script-torrent-done-enabled &&
cfg.settings.script-torrent-done-filename != null)
cfg.settings.script-torrent-done-filename ++
optional (cfg.settings.watch-dir-enabled && !cfg.settings.trash-original-torrent-files)
cfg.settings.watch-dir;
]
++ optional cfg.settings.incomplete-dir-enabled cfg.settings.incomplete-dir
++ optional (
cfg.settings.watch-dir-enabled && cfg.settings.trash-original-torrent-files
) cfg.settings.watch-dir;
BindReadOnlyPaths =
[
# No confinement done of /nix/store here like in systemd-confinement.nix,
# an AppArmor profile is provided to get a confinement based upon paths and rights.
builtins.storeDir
"/etc"
]
++ optional (
cfg.settings.script-torrent-done-enabled && cfg.settings.script-torrent-done-filename != null
) cfg.settings.script-torrent-done-filename
++ optional (
cfg.settings.watch-dir-enabled && !cfg.settings.trash-original-torrent-files
) cfg.settings.watch-dir;
StateDirectory = [
"transmission"
"transmission/${settingsDir}"
@@ -389,7 +463,11 @@ in
RemoveIPC = true;
# AF_UNIX may become usable one day:
# https://github.com/transmission/transmission/issues/441
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
RestrictAddressFamilies = [
"AF_UNIX"
"AF_INET"
"AF_INET6"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
@@ -398,7 +476,13 @@ in
# Groups in @system-service which do not contain a syscall
# listed by perf stat -e 'syscalls:sys_enter_*' transmission-daemon -f
# in tests, and seem likely not necessary for transmission-daemon.
"~@aio" "~@chown" "~@keyring" "~@memlock" "~@resources" "~@setuid" "~@timer"
"~@aio"
"~@chown"
"~@keyring"
"~@memlock"
"~@resources"
"~@setuid"
"~@timer"
# In the @privileged group, but reached when querying infos through RPC (eg. with stig).
"quotactl"
];
@@ -409,38 +493,41 @@ in
# It's useful to have transmission in path, e.g. for remote control
environment.systemPackages = [ cfg.package ];
users.users = optionalAttrs (cfg.user == "transmission") ({
users.users = optionalAttrs (cfg.user == "transmission") {
transmission = {
group = cfg.group;
uid = config.ids.uids.transmission;
description = "Transmission BitTorrent user";
home = cfg.home;
};
});
};
users.groups = optionalAttrs (cfg.group == "transmission") ({
users.groups = optionalAttrs (cfg.group == "transmission") {
transmission = {
gid = config.ids.gids.transmission;
};
});
};
networking.firewall = mkMerge [
(mkIf cfg.openPeerPorts (
if cfg.settings.peer-port-random-on-start
then
{ allowedTCPPortRanges =
[ { from = cfg.settings.peer-port-random-low;
to = cfg.settings.peer-port-random-high;
}
];
allowedUDPPortRanges =
[ { from = cfg.settings.peer-port-random-low;
to = cfg.settings.peer-port-random-high;
}
];
if cfg.settings.peer-port-random-on-start then
{
allowedTCPPortRanges = [
{
from = cfg.settings.peer-port-random-low;
to = cfg.settings.peer-port-random-high;
}
];
allowedUDPPortRanges = [
{
from = cfg.settings.peer-port-random-low;
to = cfg.settings.peer-port-random-high;
}
];
}
else
{ allowedTCPPorts = [ cfg.settings.peer-port ];
{
allowedTCPPorts = [ cfg.settings.peer-port ];
allowedUDPPorts = [ cfg.settings.peer-port ];
}
))
@@ -502,14 +589,16 @@ in
''}
}
${optionalString (cfg.settings.script-torrent-done-enabled &&
cfg.settings.script-torrent-done-filename != null) ''
# Stack transmission_directories profile on top of
# any existing profile for script-torrent-done-filename
# FIXME: to be tested as I'm not sure it works well with NoNewPrivileges=
# https://gitlab.com/apparmor/apparmor/-/wikis/AppArmorStacking#seccomp-and-no_new_privs
px ${cfg.settings.script-torrent-done-filename} -> &@{dirs},
''}
${optionalString
(cfg.settings.script-torrent-done-enabled && cfg.settings.script-torrent-done-filename != null)
''
# Stack transmission_directories profile on top of
# any existing profile for script-torrent-done-filename
# FIXME: to be tested as I'm not sure it works well with NoNewPrivileges=
# https://gitlab.com/apparmor/apparmor/-/wikis/AppArmorStacking#seccomp-and-no_new_privs
px ${cfg.settings.script-torrent-done-filename} -> &@{dirs},
''
}
${optionalString (cfg.webHome != null) ''
r ${cfg.webHome}/**,
+3 -1
View File
@@ -41,6 +41,8 @@ in {
'';
};
options.services.cage.package = mkPackageOption pkgs "cage" { };
config = mkIf cfg.enable {
# The service is partially based off of the one provided in the
@@ -64,7 +66,7 @@ in {
unitConfig.ConditionPathExists = "/dev/tty1";
serviceConfig = {
ExecStart = ''
${pkgs.cage}/bin/cage \
${cfg.package}/bin/cage \
${escapeShellArgs cfg.extraArguments} \
-- ${cfg.program}
'';
+58 -14
View File
@@ -1,12 +1,57 @@
{ config, lib, pkgs, ... }:
with lib;
let
inherit (lib)
any
attrsets
attrByPath
catAttrs
collect
concatMapStrings
concatStringsSep
escape
escapeShellArg
escapeShellArgs
hasInfix
isAttrs
isList
isStorePath
isString
mapAttrs
mapAttrsToList
optionalString
optionals
replaceStrings
splitString
substring
versionOlder
fileContents
readFile
literalExpression
literalMD
mkBefore
mkEnableOption
mkIf
mkMerge
mkOption
mkOptionType
mkPackageOption
types;
cfg = config.services.akkoma;
ex = cfg.config;
db = ex.":pleroma"."Pleroma.Repo";
web = ex.":pleroma"."Pleroma.Web.Endpoint";
format = pkgs.formats.elixirConf { elixir = cfg.package.elixirPackage; };
inherit (format.lib)
mkAtom
mkMap
mkRaw
mkTuple;
isConfined = config.systemd.services.akkoma.confinement.enable;
hasSmtp = (attrByPath [ ":pleroma" "Pleroma.Emails.Mailer" "adapter" "value" ] null ex) == "Swoosh.Adapters.SMTP";
@@ -96,16 +141,15 @@ let
passAsFile = [ "code" ];
} ''elixir "$codePath" >"$out"'');
format = pkgs.formats.elixirConf { elixir = cfg.package.elixirPackage; };
configFile = format.generate "config.exs"
(replaceSec
(attrsets.updateManyAttrsByPath [{
path = [ ":pleroma" "Pleroma.Web.Endpoint" "http" "ip" ];
update = addr:
if isAbsolutePath addr
then format.lib.mkTuple
[ (format.lib.mkAtom ":local") addr ]
else format.lib.mkRaw (erlAddr addr);
then mkTuple
[ (mkAtom ":local") addr ]
else mkRaw (erlAddr addr);
}] cfg.config));
writeShell = { name, text, runtimeInputs ? [ ] }:
@@ -282,7 +326,7 @@ let
AKKOMA_CONFIG_PATH="''${RUNTIME_DIRECTORY%%:*}/config.exs" \
ERL_EPMD_ADDRESS="${cfg.dist.address}" \
ERL_EPMD_PORT="${toString cfg.dist.epmdPort}" \
ERL_FLAGS=${lib.escapeShellArg (lib.escapeShellArgs ([
ERL_FLAGS=${escapeShellArg (escapeShellArgs ([
"-kernel" "inet_dist_use_interface" (erlAddr cfg.dist.address)
"-kernel" "inet_dist_listen_min" (toString cfg.dist.portMin)
"-kernel" "inet_dist_listen_max" (toString cfg.dist.portMax)
@@ -639,7 +683,7 @@ in {
"Pleroma.Repo" = mkOption {
type = elixirValue;
default = {
adapter = format.lib.mkRaw "Ecto.Adapters.Postgres";
adapter = mkRaw "Ecto.Adapters.Postgres";
socket_dir = "/run/postgresql";
username = cfg.user;
database = "akkoma";
@@ -769,7 +813,7 @@ in {
in {
base_url = mkOption {
type = types.nonEmptyStr;
default = if lib.versionOlder config.system.stateVersion "24.05"
default = if versionOlder config.system.stateVersion "24.05"
then "${httpConf.scheme}://${httpConf.host}:${builtins.toString httpConf.port}/media/"
else null;
defaultText = literalExpression ''
@@ -787,7 +831,7 @@ in {
":frontends" = mkOption {
type = elixirValue;
default = mapAttrs
(key: val: format.lib.mkMap { name = val.name; ref = val.ref; })
(key: val: mkMap { name = val.name; ref = val.ref; })
cfg.frontends;
defaultText = literalExpression ''
lib.mapAttrs (key: val:
@@ -816,7 +860,7 @@ in {
};
base_url = mkOption {
type = types.nullOr types.nonEmptyStr;
default = if lib.versionOlder config.system.stateVersion "24.05"
default = if versionOlder config.system.stateVersion "24.05"
then "${httpConf.scheme}://${httpConf.host}:${builtins.toString httpConf.port}"
else null;
defaultText = literalExpression ''
@@ -899,7 +943,7 @@ in {
":backends" = mkOption {
type = types.listOf elixirValue;
visible = false;
default = with format.lib; [
default = [
(mkTuple [ (mkRaw "ExSyslogger") (mkAtom ":ex_syslogger") ])
];
};
@@ -913,7 +957,7 @@ in {
level = mkOption {
type = types.nonEmptyStr;
apply = format.lib.mkAtom;
apply = mkAtom;
default = ":info";
example = ":warning";
description = ''
@@ -931,7 +975,7 @@ in {
":data_dir" = mkOption {
type = elixirValue;
internal = true;
default = format.lib.mkRaw ''
default = mkRaw ''
Path.join(System.fetch_env!("CACHE_DIRECTORY"), "tzdata")
'';
};
@@ -1136,6 +1180,6 @@ in {
};
};
meta.maintainers = with maintainers; [ mvs ];
meta.maintainers = with lib.maintainers; [ mvs ];
meta.doc = ./akkoma.md;
}
@@ -185,7 +185,7 @@ in
DB_PORT = 3306;
DB_DATABASE = "firefly";
DB_USERNAME = "firefly";
DB_PASSWORD_FILE = "/var/secrets/firefly-iii-mysql-password.txt;
DB_PASSWORD_FILE = "/var/secrets/firefly-iii-mysql-password.txt";
}
'';
type = lib.types.submodule {
@@ -226,7 +226,7 @@ in
options.sites = mkOption {
type = types.attrsOf (types.submodule siteOpts);
default = {};
description = "Specification of one or more WordPress sites to serve";
description = "Specification of one or more InvoicePlane sites to serve";
};
options.webserver = mkOption {
+5 -1
View File
@@ -204,7 +204,6 @@ in
};
"/" = {
# mixed frontend and backend requests, based on the request headers
recommendedProxySettings = true;
extraConfig = ''
set $proxpass "${ui}";
if ($http_accept = "application/activity+json") {
@@ -221,6 +220,11 @@ in
rewrite ^(.+)/+$ $1 permanent;
proxy_pass $proxpass;
# Proxied `Host` header is required to validate ActivityPub HTTP signatures for incoming events.
# The other headers are optional, for the sake of better log data.
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
'';
};
};
@@ -44,7 +44,7 @@ let
enableSshSocket = config.services.openssh.startWhenNeeded;
};
notExcluded = pkg: (!(lib.elem pkg config.environment.budgie.excludePackages));
notExcluded = pkg: utils.disablePackageByName pkg config.environment.budgie.excludePackages;
in {
meta.maintainers = lib.teams.budgie.members;
@@ -12,7 +12,7 @@ let
extraGSettingsOverrides = cfg.extraGSettingsOverrides;
};
notExcluded = pkg: (!(lib.elem (lib.getName pkg) (map lib.getName config.environment.cinnamon.excludePackages)));
notExcluded = pkg: utils.disablePackageByName pkg config.environment.cinnamon.excludePackages;
in
{
@@ -111,6 +111,7 @@ in
services.gnome.glib-networking.enable = true;
services.gnome.gnome-keyring.enable = true;
services.gvfs.enable = true;
services.power-profiles-daemon.enable = mkDefault true;
services.switcherooControl.enable = mkDefault true; # xapp-gpu-offload-helper
services.touchegg.enable = mkDefault true;
services.udisks2.enable = true;
@@ -151,9 +152,6 @@ in
# cinnamon-killer-daemon: provided by cinnamon-common
networkmanagerapplet # session requirement - also nm-applet not needed
# For a polkit authentication agent
polkit_gnome
# packages
nemo-with-extensions
gnome-online-accounts-gtk
@@ -58,7 +58,7 @@ let
enableGnomePanel = true;
} ++ cfg.flashback.customSessions;
notExcluded = pkg: mkDefault (!(lib.elem (lib.getName pkg) (map lib.getName config.environment.gnome.excludePackages)));
notExcluded = pkg: mkDefault (utils.disablePackageByName pkg config.environment.gnome.excludePackages);
in
@@ -12,7 +12,7 @@ let
extraGSettingsOverrides = cfg.extraGSettingsOverrides;
};
notExcluded = pkg: (!(lib.elem pkg config.environment.pantheon.excludePackages));
notExcluded = pkg: utils.disablePackageByName pkg config.environment.pantheon.excludePackages;
in
{
@@ -6,16 +6,17 @@ let
cfg = config.services.xserver.windowManager.exwm;
loadScript = pkgs.writeText "emacs-exwm-load" ''
${cfg.loadScript}
${optionalString cfg.enableDefaultConfig ''
(require 'exwm-config)
(exwm-config-default)
''}
'';
packages = epkgs: cfg.extraPackages epkgs ++ [ epkgs.exwm ];
exwm-emacs = pkgs.emacsWithPackages packages;
exwm-emacs = pkgs.emacs.pkgs.withPackages packages;
in
{
imports = [
(mkRemovedOptionModule [ "services" "xserver" "windowManager" "exwm" "enableDefaultConfig" ]
"The upstream EXWM project no longer provides a default configuration, instead copy (parts of) exwm-config.el to your local config.")
];
options = {
services.xserver.windowManager.exwm = {
enable = mkEnableOption "exwm";
@@ -28,15 +29,9 @@ in
'';
description = ''
Emacs lisp code to be run after loading the user's init
file. If enableDefaultConfig is true, this will be run
before loading the default config.
file.
'';
};
enableDefaultConfig = mkOption {
default = true;
type = lib.types.bool;
description = "Enable an uncustomised exwm configuration.";
};
extraPackages = mkOption {
type = types.functionTo (types.listOf types.package);
default = epkgs: [];
+4 -2
View File
@@ -15,6 +15,8 @@ in
description = ''Whether to enable the unl0kr on-screen keyboard in initrd to unlock LUKS.'';
};
package = lib.mkPackageOption pkgs "unl0kr" { };
allowVendorDrivers = lib.mkEnableOption "load optional drivers" // {
description = ''Whether to load additional drivers for certain vendors (I.E: Wacom, Intel, etc.)'';
};
@@ -85,7 +87,7 @@ in
libinput
xkeyboard_config
"${config.boot.initrd.systemd.package}/lib/systemd/systemd-reply-password"
"${pkgs.unl0kr}/bin/unl0kr"
(lib.getExe' cfg.package "unl0kr")
];
services = {
unl0kr-ask-password = {
@@ -112,7 +114,7 @@ in
do
for file in `ls $DIR/ask.*`; do
socket="$(cat "$file" | ${pkgs.gnugrep}/bin/grep "Socket=" | cut -d= -f2)"
${pkgs.unl0kr}/bin/unl0kr -v -C "/etc/unl0kr.conf" | ${config.boot.initrd.systemd.package}/lib/systemd/systemd-reply-password 1 "$socket"
${lib.getExe' cfg.package "unl0kr"} -v -C "/etc/unl0kr.conf" | ${config.boot.initrd.systemd.package}/lib/systemd/systemd-reply-password 1 "$socket"
done
done
'';
+4 -3
View File
@@ -4,6 +4,8 @@ with lib;
with utils;
let
# https://wiki.archlinux.org/index.php/fstab#Filepath_spaces
escape = string: builtins.replaceStrings [ " " "\t" ] [ "\\040" "\\011" ] string;
addCheckDesc = desc: elemType: check: types.addCheck elemType check
// { description = "${elemType.description} (with check: ${desc})"; };
@@ -136,6 +138,8 @@ let
};
config.device = lib.mkIf (config.label != null) "/dev/disk/by-label/${escape config.label}";
config.options = let
inInitrd = utils.fsNeededForBoot config;
in mkMerge [
@@ -196,11 +200,8 @@ let
];
isBindMount = fs: builtins.elem "bind" fs.options;
skipCheck = fs: fs.noCheck || fs.device == "none" || builtins.elem fs.fsType fsToSkipCheck || isBindMount fs;
# https://wiki.archlinux.org/index.php/fstab#Filepath_spaces
escape = string: builtins.replaceStrings [ " " "\t" ] [ "\\040" "\\011" ] string;
in fstabFileSystems: { }: concatMapStrings (fs:
(if fs.device != null then escape fs.device
else if fs.label != null then "/dev/disk/by-label/${escape fs.label}"
else throw "No device specified for mount point ${fs.mountPoint}.")
+ " " + escape fs.mountPoint
+ " " + fs.fsType
+1
View File
@@ -761,6 +761,7 @@ in
lib.nameValuePair "zfs-sync-${pool}" {
description = "Sync ZFS pool \"${pool}\"";
wantedBy = [ "shutdown.target" ];
before = [ "final.target" ];
unitConfig = {
DefaultDependencies = false;
};
+280 -154
View File
@@ -1,4 +1,10 @@
{ config, options, lib, pkgs, ... }:
{
config,
options,
lib,
pkgs,
...
}:
with lib;
let
@@ -8,7 +14,8 @@ let
defaultBackend = options.virtualisation.oci-containers.backend.default;
containerOptions =
{ ... }: {
{ ... }:
{
options = {
@@ -77,8 +84,8 @@ let
};
cmd = mkOption {
type = with types; listOf str;
default = [];
type = with types; listOf str;
default = [ ];
description = "Commandline arguments to pass to the image's entrypoint.";
example = literalExpression ''
["--port=9000"]
@@ -87,7 +94,7 @@ let
labels = mkOption {
type = with types; attrsOf str;
default = {};
default = { };
description = "Labels to attach to the container at runtime.";
example = literalExpression ''
{
@@ -105,26 +112,26 @@ let
environment = mkOption {
type = with types; attrsOf str;
default = {};
default = { };
description = "Environment variables to set for this container.";
example = literalExpression ''
{
DATABASE_HOST = "db.example.com";
DATABASE_PORT = "3306";
}
'';
'';
};
environmentFiles = mkOption {
type = with types; listOf path;
default = [];
default = [ ];
description = "Environment files for this container.";
example = literalExpression ''
[
/path/to/.env
/path/to/.env.secret
]
'';
'';
};
log-driver = mkOption {
@@ -147,7 +154,7 @@ let
ports = mkOption {
type = with types; listOf str;
default = [];
default = [ ];
description = ''
Network ports to publish from the container to the outer host.
@@ -194,7 +201,7 @@ let
volumes = mkOption {
type = with types; listOf str;
default = [];
default = [ ];
description = ''
List of volumes to attach to this container.
@@ -222,7 +229,7 @@ let
dependsOn = mkOption {
type = with types; listOf str;
default = [];
default = [ ];
description = ''
Define which other containers this one depends on. They will be added to both After and Requires for the unit.
@@ -247,14 +254,17 @@ let
preRunExtraOptions = mkOption {
type = with types; listOf str;
default = [];
default = [ ];
description = "Extra options for {command}`${defaultBackend}` that go before the `run` argument.";
example = [ "--runtime" "runsc" ];
example = [
"--runtime"
"runsc"
];
};
extraOptions = mkOption {
type = with types; listOf str;
default = [];
default = [ ];
description = "Extra options for {command}`${defaultBackend} run`.";
example = literalExpression ''
["--network=host"]
@@ -262,177 +272,293 @@ let
};
autoStart = mkOption {
type = types.bool;
type = with types; bool;
default = true;
description = ''
When enabled, the container is automatically started on boot.
If this option is set to false, the container has to be started on-demand via its service.
'';
};
pull = mkOption {
type =
with types;
enum [
"always"
"missing"
"never"
"newer"
];
default = "missing";
description = ''
Image pull policy for the container. Must be one of: always, missing, never, newer
'';
};
capAdd = mkOption {
type = with types; lazyAttrsOf (nullOr bool);
default = { };
description = ''
Capabilities to add to container
'';
example = literalExpression ''
{
SYS_ADMIN = true;
{
'';
};
capDrop = mkOption {
type = with types; lazyAttrsOf (nullOr bool);
default = { };
description = ''
Capabilities to drop from container
'';
example = literalExpression ''
{
SYS_ADMIN = true;
{
'';
};
devices = mkOption {
type = with types; listOf str;
default = [ ];
description = ''
List of devices to attach to this container.
'';
example = literalExpression ''
[
"/dev/dri:/dev/dri"
]
'';
};
privileged = mkOption {
type = with types; bool;
default = false;
description = ''
Give extended privileges to the container
'';
};
networks = mkOption {
type = with types; listOf str;
default = [ ];
description = ''
Networks to attach the container to
'';
};
};
};
isValidLogin = login: login.username != null && login.passwordFile != null && login.registry != null;
isValidLogin =
login: login.username != null && login.passwordFile != null && login.registry != null;
mkService = name: container: let
dependsOn = map (x: "${cfg.backend}-${x}.service") container.dependsOn;
escapedName = escapeShellArg name;
preStartScript = pkgs.writeShellApplication {
name = "pre-start";
runtimeInputs = [ ];
text = ''
${cfg.backend} rm -f ${name} || true
${optionalString (isValidLogin container.login) ''
# try logging in, if it fails, check if image exists locally
${cfg.backend} login \
${container.login.registry} \
--username ${container.login.username} \
--password-stdin < ${container.login.passwordFile} \
|| ${cfg.backend} image inspect ${container.image} >/dev/null \
|| { echo "image doesn't exist locally and login failed" >&2 ; exit 1; }
''}
${optionalString (container.imageFile != null) ''
${cfg.backend} load -i ${container.imageFile}
''}
${optionalString (container.imageStream != null) ''
${container.imageStream} | ${cfg.backend} load
''}
${optionalString (cfg.backend == "podman") ''
rm -f /run/podman-${escapedName}.ctr-id
''}
'';
mkService =
name: container:
let
dependsOn = map (x: "${cfg.backend}-${x}.service") container.dependsOn;
escapedName = escapeShellArg name;
preStartScript = pkgs.writeShellApplication {
name = "pre-start";
runtimeInputs = [ ];
text = ''
${cfg.backend} rm -f ${name} || true
${optionalString (isValidLogin container.login) ''
# try logging in, if it fails, check if image exists locally
${cfg.backend} login \
${container.login.registry} \
--username ${container.login.username} \
--password-stdin < ${container.login.passwordFile} \
|| ${cfg.backend} image inspect ${container.image} >/dev/null \
|| { echo "image doesn't exist locally and login failed" >&2 ; exit 1; }
''}
${optionalString (container.imageFile != null) ''
${cfg.backend} load -i ${container.imageFile}
''}
${optionalString (container.imageStream != null) ''
${container.imageStream} | ${cfg.backend} load
''}
${optionalString (cfg.backend == "podman") ''
rm -f /run/podman-${escapedName}.ctr-id
''}
'';
};
in
{
wantedBy = [ ] ++ optional (container.autoStart) "multi-user.target";
wants = lib.optional (
container.imageFile == null && container.imageStream == null
) "network-online.target";
after =
lib.optionals (cfg.backend == "docker") [
"docker.service"
"docker.socket"
]
# if imageFile or imageStream is not set, the service needs the network to download the image from the registry
++ lib.optionals (container.imageFile == null && container.imageStream == null) [
"network-online.target"
]
++ dependsOn;
requires = dependsOn;
environment = proxy_env;
path =
if cfg.backend == "docker" then
[ config.virtualisation.docker.package ]
else if cfg.backend == "podman" then
[ config.virtualisation.podman.package ]
else
throw "Unhandled backend: ${cfg.backend}";
script = concatStringsSep " \\\n " (
[
"exec ${cfg.backend} "
]
++ map escapeShellArg container.preRunExtraOptions
++ [
"run"
"--rm"
"--name=${escapedName}"
"--log-driver=${container.log-driver}"
]
++ optional (container.entrypoint != null) "--entrypoint=${escapeShellArg container.entrypoint}"
++ optional (container.hostname != null) "--hostname=${escapeShellArg container.hostname}"
++ lib.optionals (cfg.backend == "podman") [
"--cidfile=/run/podman-${escapedName}.ctr-id"
"--cgroups=no-conmon"
"--sdnotify=conmon"
"-d"
"--replace"
]
++ (mapAttrsToList (k: v: "-e ${escapeShellArg k}=${escapeShellArg v}") container.environment)
++ map (f: "--env-file ${escapeShellArg f}") container.environmentFiles
++ map (p: "-p ${escapeShellArg p}") container.ports
++ optional (container.user != null) "-u ${escapeShellArg container.user}"
++ map (v: "-v ${escapeShellArg v}") container.volumes
++ (mapAttrsToList (k: v: "-l ${escapeShellArg k}=${escapeShellArg v}") container.labels)
++ optional (container.workdir != null) "-w ${escapeShellArg container.workdir}"
++ optional (container.privileged) "--privileged"
++ mapAttrsToList (k: _: "--cap-add=${escapeShellArg k}") (
filterAttrs (_: v: v == true) container.capAdd
)
++ mapAttrsToList (k: _: "--cap-drop=${escapeShellArg k}") (
filterAttrs (_: v: v == true) container.capDrop
)
++ map (d: "--device=${escapeShellArg d}") container.devices
++ map (n: "--network=${escapeShellArg n}") container.networks
++ [ "--pull ${escapeShellArg container.pull}" ]
++ map escapeShellArg container.extraOptions
++ [ container.image ]
++ map escapeShellArg container.cmd
);
preStop =
if cfg.backend == "podman" then
"podman stop --ignore --cidfile=/run/podman-${escapedName}.ctr-id"
else
"${cfg.backend} stop ${name} || true";
postStop =
if cfg.backend == "podman" then
"podman rm -f --ignore --cidfile=/run/podman-${escapedName}.ctr-id"
else
"${cfg.backend} rm -f ${name} || true";
serviceConfig =
{
### There is no generalized way of supporting `reload` for docker
### containers. Some containers may respond well to SIGHUP sent to their
### init process, but it is not guaranteed; some apps have other reload
### mechanisms, some don't have a reload signal at all, and some docker
### images just have broken signal handling. The best compromise in this
### case is probably to leave ExecReload undefined, so `systemctl reload`
### will at least result in an error instead of potentially undefined
### behaviour.
###
### Advanced users can still override this part of the unit to implement
### a custom reload handler, since the result of all this is a normal
### systemd service from the perspective of the NixOS module system.
###
# ExecReload = ...;
###
ExecStartPre = [ "${preStartScript}/bin/pre-start" ];
TimeoutStartSec = 0;
TimeoutStopSec = 120;
Restart = "always";
}
// optionalAttrs (cfg.backend == "podman") {
Environment = "PODMAN_SYSTEMD_UNIT=podman-${name}.service";
Type = "notify";
NotifyAccess = "all";
};
};
in {
wantedBy = [] ++ optional (container.autoStart) "multi-user.target";
wants = lib.optional (container.imageFile == null && container.imageStream == null) "network-online.target";
after = lib.optionals (cfg.backend == "docker") [ "docker.service" "docker.socket" ]
# if imageFile or imageStream is not set, the service needs the network to download the image from the registry
++ lib.optionals (container.imageFile == null && container.imageStream == null) [ "network-online.target" ]
++ dependsOn;
requires = dependsOn;
environment = proxy_env;
path =
if cfg.backend == "docker" then [ config.virtualisation.docker.package ]
else if cfg.backend == "podman" then [ config.virtualisation.podman.package ]
else throw "Unhandled backend: ${cfg.backend}";
script = concatStringsSep " \\\n " ([
"exec ${cfg.backend} "
] ++ map escapeShellArg container.preRunExtraOptions ++ [
"run"
"--rm"
"--name=${escapedName}"
"--log-driver=${container.log-driver}"
] ++ optional (container.entrypoint != null)
"--entrypoint=${escapeShellArg container.entrypoint}"
++ optional (container.hostname != null)
"--hostname=${escapeShellArg container.hostname}"
++ lib.optionals (cfg.backend == "podman") [
"--cidfile=/run/podman-${escapedName}.ctr-id"
"--cgroups=no-conmon"
"--sdnotify=conmon"
"-d"
"--replace"
] ++ (mapAttrsToList (k: v: "-e ${escapeShellArg k}=${escapeShellArg v}") container.environment)
++ map (f: "--env-file ${escapeShellArg f}") container.environmentFiles
++ map (p: "-p ${escapeShellArg p}") container.ports
++ optional (container.user != null) "-u ${escapeShellArg container.user}"
++ map (v: "-v ${escapeShellArg v}") container.volumes
++ (mapAttrsToList (k: v: "-l ${escapeShellArg k}=${escapeShellArg v}") container.labels)
++ optional (container.workdir != null) "-w ${escapeShellArg container.workdir}"
++ map escapeShellArg container.extraOptions
++ [container.image]
++ map escapeShellArg container.cmd
);
preStop = if cfg.backend == "podman"
then "podman stop --ignore --cidfile=/run/podman-${escapedName}.ctr-id"
else "${cfg.backend} stop ${name} || true";
postStop = if cfg.backend == "podman"
then "podman rm -f --ignore --cidfile=/run/podman-${escapedName}.ctr-id"
else "${cfg.backend} rm -f ${name} || true";
serviceConfig = {
### There is no generalized way of supporting `reload` for docker
### containers. Some containers may respond well to SIGHUP sent to their
### init process, but it is not guaranteed; some apps have other reload
### mechanisms, some don't have a reload signal at all, and some docker
### images just have broken signal handling. The best compromise in this
### case is probably to leave ExecReload undefined, so `systemctl reload`
### will at least result in an error instead of potentially undefined
### behaviour.
###
### Advanced users can still override this part of the unit to implement
### a custom reload handler, since the result of all this is a normal
### systemd service from the perspective of the NixOS module system.
###
# ExecReload = ...;
###
ExecStartPre = [ "${preStartScript}/bin/pre-start" ];
TimeoutStartSec = 0;
TimeoutStopSec = 120;
Restart = "always";
} // optionalAttrs (cfg.backend == "podman") {
Environment="PODMAN_SYSTEMD_UNIT=podman-${name}.service";
Type="notify";
NotifyAccess="all";
};
};
in {
in
{
imports = [
(
lib.mkChangedOptionModule
[ "docker-containers" ]
[ "virtualisation" "oci-containers" ]
(oldcfg: {
backend = "docker";
containers = lib.mapAttrs (n: v: builtins.removeAttrs (v // {
extraOptions = v.extraDockerOptions or [];
}) [ "extraDockerOptions" ]) oldcfg.docker-containers;
})
)
(lib.mkChangedOptionModule [ "docker-containers" ] [ "virtualisation" "oci-containers" ] (oldcfg: {
backend = "docker";
containers = lib.mapAttrs (
n: v:
builtins.removeAttrs (
v
// {
extraOptions = v.extraDockerOptions or [ ];
}
) [ "extraDockerOptions" ]
) oldcfg.docker-containers;
}))
];
options.virtualisation.oci-containers = {
backend = mkOption {
type = types.enum [ "podman" "docker" ];
type = types.enum [
"podman"
"docker"
];
default = if versionAtLeast config.system.stateVersion "22.05" then "podman" else "docker";
description = "The underlying Docker implementation to use.";
};
containers = mkOption {
default = {};
default = { };
type = types.attrsOf (types.submodule containerOptions);
description = "OCI (Docker) containers to run as systemd services.";
};
};
config = lib.mkIf (cfg.containers != {}) (lib.mkMerge [
{
systemd.services = mapAttrs' (n: v: nameValuePair "${cfg.backend}-${n}" (mkService n v)) cfg.containers;
config = lib.mkIf (cfg.containers != { }) (
lib.mkMerge [
{
systemd.services = mapAttrs' (
n: v: nameValuePair "${cfg.backend}-${n}" (mkService n v)
) cfg.containers;
assertions =
let
toAssertion = _: { imageFile, imageStream, ... }:
{ assertion = imageFile == null || imageStream == null;
assertions =
let
toAssertion =
_:
{ imageFile, imageStream, ... }:
{
assertion = imageFile == null || imageStream == null;
message = "You can only define one of imageFile and imageStream";
};
message = "You can only define one of imageFile and imageStream";
};
in
in
lib.mapAttrsToList toAssertion cfg.containers;
}
(lib.mkIf (cfg.backend == "podman") {
virtualisation.podman.enable = true;
})
(lib.mkIf (cfg.backend == "docker") {
virtualisation.docker.enable = true;
})
]);
}
(lib.mkIf (cfg.backend == "podman") {
virtualisation.podman.enable = true;
})
(lib.mkIf (cfg.backend == "docker") {
virtualisation.docker.enable = true;
})
]
);
}
+1
View File
@@ -563,6 +563,7 @@ in {
lomiri-clock-app = runTest ./lomiri-clock-app.nix;
lomiri-docviewer-app = runTest ./lomiri-docviewer-app.nix;
lomiri-filemanager-app = runTest ./lomiri-filemanager-app.nix;
lomiri-mediaplayer-app = runTest ./lomiri-mediaplayer-app.nix;
lomiri-gallery-app = runTest ./lomiri-gallery-app.nix;
lomiri-system-settings = handleTest ./lomiri-system-settings.nix {};
lorri = handleTest ./lorri/default.nix {};
+43 -30
View File
@@ -85,11 +85,19 @@ let
let
config = (import ../lib/eval-config.nix {
inherit system;
modules =
[ ../modules/installer/netboot/netboot.nix
../modules/testing/test-instrumentation.nix
{ key = "serial"; }
];
modules = [
../modules/installer/netboot/netboot.nix
../modules/testing/test-instrumentation.nix
{
boot.kernelParams = [
"serial"
"live.nixos.passwordHash=$6$jnwR50SkbLYEq/Vp$wmggwioAkfmwuYqd5hIfatZWS/bO6hewzNIwIrWcgdh7k/fhUzZT29Vil3ioMo94sdji/nipbzwEpxecLZw0d0" # "password"
];
}
{
key = "serial";
}
];
}).config;
ipxeBootDir = pkgs.symlinkJoin {
name = "ipxeBootDir";
@@ -103,34 +111,39 @@ let
pxe = ipxeBootDir;
} // extraConfig);
in
makeTest {
name = "boot-netboot-" + name;
nodes = { };
testScript = ''
machine = create_machine("${startCommand}")
machine.start()
machine.wait_for_unit("multi-user.target")
machine.shutdown()
'';
};
in {
uefiCdrom = makeBootTest "uefi-cdrom" {
uefi = true;
cdrom = "${iso}/iso/${iso.isoName}";
makeTest {
name = "boot-netboot-" + name;
nodes = { };
testScript = ''
machine = create_machine("${startCommand}")
machine.start()
machine.wait_for_unit("multi-user.target")
machine.succeed("grep 'serial' /proc/cmdline")
machine.succeed("grep 'live.nixos.passwordHash' /proc/cmdline")
machine.succeed("grep '$6$jnwR50SkbLYEq/Vp$wmggwioAkfmwuYqd5hIfatZWS/bO6hewzNIwIrWcgdh7k/fhUzZT29Vil3ioMo94sdji/nipbzwEpxecLZw0d0' /etc/shadow")
machine.shutdown()
'';
};
in
{
uefiCdrom = makeBootTest "uefi-cdrom" {
uefi = true;
cdrom = "${iso}/iso/${iso.isoName}";
};
uefiUsb = makeBootTest "uefi-usb" {
uefi = true;
usb = "${iso}/iso/${iso.isoName}";
};
uefiUsb = makeBootTest "uefi-usb" {
uefi = true;
usb = "${iso}/iso/${iso.isoName}";
};
uefiNetboot = makeNetbootTest "uefi" {
uefi = true;
};
} // lib.optionalAttrs (pkgs.stdenv.hostPlatform.system == "x86_64-linux") {
biosCdrom = makeBootTest "bios-cdrom" {
cdrom = "${iso}/iso/${iso.isoName}";
};
uefiNetboot = makeNetbootTest "uefi" {
uefi = true;
};
}
// lib.optionalAttrs (pkgs.stdenv.hostPlatform.system == "x86_64-linux") {
biosCdrom = makeBootTest "bios-cdrom" {
cdrom = "${iso}/iso/${iso.isoName}";
};
biosUsb = makeBootTest "bios-usb" {
usb = "${iso}/iso/${iso.isoName}";
+77
View File
@@ -0,0 +1,77 @@
{ lib, ... }:
let
ocrContent = "Video Test";
videoFile = "test.webm";
in
{
name = "lomiri-mediaplayer-app-standalone";
meta.maintainers = lib.teams.lomiri.members;
nodes.machine =
{ config, pkgs, ... }:
{
imports = [ ./common/x11.nix ];
services.xserver.enable = true;
environment = {
# Setup video
etc."${videoFile}".source =
pkgs.runCommand videoFile
{
nativeBuildInputs = with pkgs; [
ffmpeg # produce video for OCR
(imagemagick.override { ghostscriptSupport = true; }) # produce OCR-able image
];
}
''
magick -size 400x400 canvas:white -pointsize 40 -fill black -annotate +100+100 '${ocrContent}' output.png
ffmpeg -re -loop 1 -i output.png -c:v libvpx -b:v 100K -t 120 $out -loglevel fatal
'';
systemPackages = with pkgs.lomiri; [
suru-icon-theme
lomiri-mediaplayer-app
];
variables = {
UITK_ICON_THEME = "suru";
};
};
i18n.supportedLocales = [ "all" ];
fonts = {
packages = with pkgs; [
# Intended font & helps with OCR
ubuntu-classic
];
};
};
enableOCR = true;
testScript = ''
machine.wait_for_x()
with subtest("lomiri mediaplayer launches"):
machine.succeed("lomiri-mediaplayer-app >&2 &")
machine.wait_for_text("Choose from")
machine.screenshot("lomiri-mediaplayer_open")
machine.succeed("pkill -f lomiri-mediaplayer-app")
with subtest("lomiri mediaplayer plays video"):
machine.succeed("lomiri-mediaplayer-app /etc/${videoFile} >&2 &")
machine.wait_for_text("${ocrContent}")
machine.screenshot("lomiri-mediaplayer_playback")
machine.succeed("pkill -f lomiri-mediaplayer-app")
with subtest("lomiri mediaplayer localisation works"):
# OCR struggles with finding identifying the translated window title, and lomiri-content-hub QML isn't translated
# Cause an error, and look for the error popup
machine.succeed("touch invalid.mp4")
machine.succeed("env LANG=de_DE.UTF-8 lomiri-mediaplayer-app invalid.mp4 >&2 &")
machine.wait_for_text("Fehler")
machine.screenshot("lomiri-mediaplayer_localised")
'';
}
@@ -120,12 +120,13 @@ let
};
};
testScript = ''
start_all()
client.wait_for_unit("network.target")
router.start()
router.systemctl("start network-online.target")
router.wait_for_unit("network-online.target")
client.start()
client.wait_for_unit("network.target")
with subtest("Wait until we have an ip address on each interface"):
client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q '192.168.1'")
client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q 'fd00:1234:5678:1:'")
+17
View File
@@ -22,6 +22,16 @@ let
image = "nginx-container";
imageStream = pkgs.dockerTools.examples.nginxStream;
ports = ["8181:80"];
capAdd = {
CAP_AUDIT_READ = true;
};
capDrop = {
CAP_AUDIT_WRITE = true;
};
privileged = false;
devices = [
"/dev/random:/dev/random"
];
};
};
@@ -32,11 +42,18 @@ let
};
testScript = ''
import json
start_all()
${backend}.wait_for_unit("${backend}-nginx.service")
${backend}.wait_for_open_port(8181)
${backend}.wait_until_succeeds("curl -f http://localhost:8181 | grep Hello")
output = json.loads(${backend}.succeed("${backend} inspect nginx --format json").strip())[0]
${backend}.succeed("systemctl stop ${backend}-nginx.service", timeout=10)
assert output['HostConfig']['CapAdd'] == ["CAP_AUDIT_READ"]
assert output['HostConfig']['CapDrop'] == ${if backend == "docker" then "[\"CAP_AUDIT_WRITE\"]" else "[]"} # Rootless podman runs with no capabilities so it cannot drop them
assert output['HostConfig']['Privileged'] == False
assert output['HostConfig']['Devices'] == [{'PathOnHost': '/dev/random', 'PathInContainer': '/dev/random', 'CgroupPermissions': '${if backend == "docker" then "rwm" else ""}'}]
'';
};
+32
View File
@@ -804,6 +804,38 @@ let
'';
};
mqtt = {
exporterConfig = {
enable = true;
environmentFile = pkgs.writeText "mqtt-exporter-envfile" ''
MQTT_PASSWORD=testpassword
'';
};
metricProvider = {
services.mosquitto = {
enable = true;
listeners = [{
users.exporter = {
acl = [ "read #" ];
passwordFile = pkgs.writeText "mosquitto-password" "testpassword";
};
}];
};
systemd.services.prometheus-mqtt-exporter ={
wants = [ "mosquitto.service" ];
after = [ "mosquitto.service" ];
};
};
exporterTest = ''
wait_for_unit("mosquitto.service")
wait_for_unit("prometheus-mqtt-exporter.service")
wait_for_open_port(9000)
succeed(
"curl -sSf http://localhost:9000/metrics | grep '^python_info'"
)
'';
};
mysqld = {
exporterConfig = {
enable = true;
+1
View File
@@ -7,4 +7,5 @@
{
remote-write = import ./remote-write.nix { inherit system pkgs; };
vmalert = import ./vmalert.nix { inherit system pkgs; };
external-promscrape-config = import ./external-promscrape-config.nix { inherit system pkgs; };
}
@@ -0,0 +1,82 @@
import ../make-test-python.nix (
{
lib,
pkgs,
...
}:
let
nodeExporterPort = 9100;
promscrapeConfig = {
global = {
scrape_interval = "2s";
};
scrape_configs = [
{
job_name = "node";
static_configs = [
{
targets = [
"node:${toString nodeExporterPort}"
];
}
];
}
];
};
settingsFormat = pkgs.formats.yaml { };
promscrapeConfigYaml = settingsFormat.generate "prometheusConfig.yaml" promscrapeConfig;
in
{
name = "victoriametrics-external-promscrape-config";
meta = with pkgs.lib.maintainers; {
maintainers = [
ryan4yin
];
};
nodes = {
victoriametrics =
{
config,
pkgs,
...
}:
{
environment.systemPackages = [ pkgs.jq ];
networking.firewall.allowedTCPPorts = [ 8428 ];
services.victoriametrics = {
enable = true;
extraOptions = [
"-promscrape.config=${toString promscrapeConfigYaml}"
];
};
};
node =
{ ... }:
{
services.prometheus.exporters.node = {
enable = true;
openFirewall = true;
};
};
};
testScript = ''
node.wait_for_unit("prometheus-node-exporter")
node.wait_for_open_port(${toString nodeExporterPort})
victoriametrics.wait_for_unit("victoriametrics")
victoriametrics.wait_for_open_port(8428)
promscrape_config = victoriametrics.succeed("journalctl -u victoriametrics -o cat | grep 'promscrape.config'")
assert '${toString promscrapeConfigYaml}' in promscrape_config
victoriametrics.wait_until_succeeds(
"curl -sf 'http://localhost:8428/api/v1/query?query=node_exporter_build_info\{instance=\"node:9100\"\}' | "
+ "jq '.data.result[0].value[1]' | grep '\"1\"'"
)
'';
}
)
+15 -6
View File
@@ -492,7 +492,9 @@ Examples going from bad to best practices:
Sometimes, changes are needed to the source to allow building a derivation in nixpkgs, or to get earlier access to an upstream fix or improvement.
When using the `patches` parameter to `mkDerivation`, make sure the patch name clearly describes the reason for the patch, or add a comment.
Patches already merged upstream or published elsewhere should be retrieved using `fetchpatch`.
### Fetching patches
In the interest of keeping our maintenance burden and the size of Nixpkgs to a minimum, patches already merged upstream or published elsewhere _should_ be retrieved using `fetchpatch`:
```nix
{
@@ -506,15 +508,22 @@ Patches already merged upstream or published elsewhere should be retrieved using
}
```
Otherwise, you can add a `.patch` file to the `nixpkgs` repository.
In the interest of keeping our maintenance burden and the size of nixpkgs to a minimum, only do this for patches that are unique to `nixpkgs` or that have been proposed upstream but are not merged yet, cannot be easily fetched or have a high chance to disappear in the future due to unstable or unreliable URLs.
The latter avoids link rot when the upstream abandons, squashes or rebases their change, in which case the commit may get garbage-collected.
If a patch is available online but does not cleanly apply, it can be modified in some fixed ways by using additional optional arguments for `fetchpatch`. Check [the `fetchpatch` reference](https://nixos.org/manual/nixpkgs/unstable/#fetchpatch) for details.
### Vendoring patches
In the following cases, a `.patch` file _should_ be added to Nixpkgs repository, instead of retrieved:
- solves problems unique to packaging in Nixpkgs
- is already proposed upstream but not merged yet
- cannot be fetched easily
- has a high chance to disappear in the future due to unstable or unreliable URLs
The latter avoids link rot when the upstream abandons, squashes or rebases their change, in which case the commit may get garbage-collected.
```nix
{
patches = [ ./0001-changes.patch ];
patches = [ ./0001-add-missing-include.patch ];
}
```
@@ -1,69 +0,0 @@
{ depname
, version
, src
, sourceRoot
, stdenv
, lib
, patches ? []
, extraPostPatch ? ""
, buildInputs ? []
}:
let
rebuildscriptName = if stdenv.hostPlatform.isLinux then
"build_linux"
else if stdenv.hostPlatform.isDarwin then
"build_macos"
else throw "Don't know how to rebuild FamiStudio's vendored ${depname} for ${stdenv.hostPlatform.system}";
in
stdenv.mkDerivation {
pname = "famistudio-nativedep-${depname}";
inherit version src sourceRoot patches buildInputs;
postPatch = let
libnameBase = lib.optionalString stdenv.hostPlatform.isLinux "lib" + depname;
in ''
# Use one name for build script, eases with patching
mv ${rebuildscriptName}.sh build.sh
# Scripts use hardcoded compilers and try to copy built libraries into FamiStudio's build tree
# Not all scripts use the same compiler, so don't fail on replacing that
substituteInPlace build.sh \
--replace-fail '../../FamiStudio/' "$out/lib/" \
--replace-quiet 'g++' "$CXX"
# Replacing gcc via sed, would break -static-libgcc otherwise
sed -i -e "s/^gcc/$CC/g" build.sh
'' + lib.optionalString stdenv.hostPlatform.isDarwin ''
# Darwin rebuild scripts try to make a universal2 dylib
# - build dylib for non-hostPlatform
# - copy built library into special directory for later packaging script
# - join two dylibs together into a universal2 dylib
# Remove everything we don't need
sed -ri \
-e '/-target ${if stdenv.hostPlatform.isx86_64 then "arm64" else "x86_64"}/d' \
-e '/..\/..\/Setup/d' \
build.sh
# Replace joining multi-arch dylibs with copying dylib for target arch
substituteInPlace build.sh \
--replace-fail 'lipo -create -output ${libnameBase}.dylib' 'cp ${libnameBase}_${if stdenv.hostPlatform.isx86_64 then "x86_64" else "arm64"}.dylib ${libnameBase}.dylib #'
'' + extraPostPatch;
dontConfigure = true;
dontInstall = true; # rebuild script automatically installs
buildPhase = ''
runHook preBuild
mkdir -p $out/lib
# Delete all prebuilt libraries, make sure everything is rebuilt
find . -name '*.so' -or -name '*.dylib' -or -name '*.a' -delete
# When calling normally, an error won't cause derivation to fail
source ./build.sh
runHook postBuild
'';
}
@@ -1,125 +0,0 @@
{ stdenv
, lib
, buildDotnetModule
, dotnetCorePackages
, callPackage
, fetchFromGitHub
, ffmpeg
, glfw
, libglvnd
, libogg
, libvorbis
, openal
, portaudio
, rtmidi
}:
let
csprojName = if stdenv.hostPlatform.isLinux then
"FamiStudio.Linux"
else if stdenv.hostPlatform.isDarwin then
"FamiStudio.Mac"
else throw "Don't know how to build FamiStudio for ${stdenv.hostPlatform.system}";
in
buildDotnetModule rec {
pname = "famistudio";
version = "4.2.1";
src = fetchFromGitHub {
owner = "BleuBleu";
repo = "FamiStudio";
rev = "refs/tags/${version}";
hash = "sha256-WYy/6cWQg3Ayok/eAdnvlWAvdcuhy/sdlWOVvaYcPkc=";
};
postPatch = let
libname = library: "${library}${stdenv.hostPlatform.extensions.sharedLibrary}";
buildNativeWrapper = args: callPackage ./build-native-wrapper.nix (args // {
inherit version src;
sourceRoot = "${src.name}/ThirdParty/${args.depname}";
});
nativeWrapperToReplaceFormat = args: let
libPrefix = lib.optionalString stdenv.hostPlatform.isLinux "lib";
in {
package = buildNativeWrapper args;
expectedName = "${libPrefix}${args.depname}";
ourName = "${libPrefix}${args.depname}";
};
librariesToReplace = [
# Unmodified native libraries that we can fully substitute
{ package = glfw; expectedName = "libglfw"; ourName = "libglfw"; }
{ package = rtmidi; expectedName = "librtmidi"; ourName = "librtmidi"; }
] ++ lib.optionals stdenv.hostPlatform.isLinux [
{ package = openal; expectedName = "libopenal32"; ourName = "libopenal"; }
] ++ lib.optionals stdenv.hostPlatform.isDarwin [
{ package = portaudio; expectedName = "libportaudio.2"; ourName = "libportaudio.2"; }
] ++ [
# Native libraries, with extra code for the C# wrapping
(nativeWrapperToReplaceFormat { depname = "GifDec"; })
(nativeWrapperToReplaceFormat { depname = "NesSndEmu"; })
(nativeWrapperToReplaceFormat { depname = "NotSoFatso"; extraPostPatch = ''
# C++17 does not allow register storage class specifier
substituteInPlace build.sh \
--replace-fail "$CXX" "$CXX -std=c++14"
''; })
(nativeWrapperToReplaceFormat { depname = "ShineMp3"; })
(nativeWrapperToReplaceFormat { depname = "Stb"; })
(nativeWrapperToReplaceFormat { depname = "Vorbis"; buildInputs = [ libogg libvorbis ]; })
];
libraryReplaceArgs = lib.strings.concatMapStringsSep " "
(library: "--replace-fail '${libname library.expectedName}' '${lib.getLib library.package}/lib/${libname library.ourName}'")
librariesToReplace;
in ''
# Don't use any prebuilt libraries
rm FamiStudio/*.{dll,dylib,so*}
# Replace copying of vendored prebuilt native libraries with copying of our native libraries
substituteInPlace ${projectFile} ${libraryReplaceArgs}
# Un-hardcode target platform if set
sed -i -e '/PlatformTarget/d' ${projectFile}
# Don't require a special name to be preserved, our OpenAL isn't 32-bit
substituteInPlace FamiStudio/Source/AudioStreams/OpenALStream.cs \
--replace-fail 'libopenal32' 'libopenal'
'';
projectFile = "FamiStudio/${csprojName}.csproj";
nugetDeps = ./deps.nix;
dotnet-sdk = dotnetCorePackages.sdk_7_0;
dotnet-runtime = dotnetCorePackages.runtime_7_0;
runtimeDeps = lib.optionals stdenv.hostPlatform.isLinux [
libglvnd
];
executables = [ "FamiStudio" ];
postInstall = ''
mkdir -p $out/share/famistudio
for datdir in Setup/Demo\ {Instruments,Songs}; do
cp -R "$datdir" $out/share/famistudio/
done
'';
postFixup = ''
# FFMpeg looked up from PATH
wrapProgram $out/bin/FamiStudio \
--prefix PATH : ${lib.makeBinPath [ ffmpeg ]}
'';
passthru.updateScript = ./update.sh;
meta = with lib; {
homepage = "https://famistudio.org/";
description = "NES Music Editor";
longDescription = ''
FamiStudio is very simple music editor for the Nintendo Entertainment System
or Famicom. It is targeted at both chiptune artists and NES homebrewers.
'';
license = licenses.mit;
maintainers = with maintainers; [ OPNA2608 ];
platforms = platforms.unix;
mainProgram = "FamiStudio";
};
}
+1 -1
View File
@@ -63,7 +63,7 @@ mkDerivation rec {
owner = "mixxxdj";
repo = "mixxx";
rev = version;
hash = "sha256-foY4K1rSth0GUjM1xpctI3fpavVjGoPMnRN2tT4Lsqg=";
hash = "sha256-YfpFRLosIIND+HnZN+76ZY0dQqEJaFkWZS84gZOCdfc=";
};
nativeBuildInputs = [ cmake pkg-config wrapGAppsHook3 ];
+1 -1
View File
@@ -25,7 +25,7 @@ appimageTools.wrapType2 {
--replace 'Exec=AppRun' 'Exec=${pname}'
source "${makeWrapper}/nix-support/setup-hook"
wrapProgram "$out/bin/plexamp" \
--add-flags "\''${NIXOS_OZONE_WL:+\''${WAYLAND_DISPLAY:+--ozone-platform-hint=auto --enable-features=WaylandWindowDecorations --enable-wayland-ime}}"
--add-flags "\''${NIXOS_OZONE_WL:+\''${WAYLAND_DISPLAY:+--ozone-platform-hint=auto --enable-features=WaylandWindowDecorations --enable-wayland-ime=true}}"
'';
passthru.updateScript = ./update-plexamp.sh;
+2 -2
View File
@@ -13,14 +13,14 @@
stdenv.mkDerivation (finalAttrs: {
pname = "qpwgraph";
version = "0.7.9";
version = "0.8.0";
src = fetchFromGitLab {
domain = "gitlab.freedesktop.org";
owner = "rncbc";
repo = "qpwgraph";
rev = "v${finalAttrs.version}";
sha256 = "sha256-VEJbjrXSMERGUyfIo43hg7v/S7lRzzvQ4gmwlqOMgAQ=";
sha256 = "sha256-jB2mMLwJ1e/fIsf1R9Wd0stwp/RQH6f9pkF1qQX72Aw=";
};
nativeBuildInputs = [ cmake pkg-config wrapQtAppsHook ];
+84 -86
View File
@@ -1,57 +1,52 @@
{ stdenv
, lib
, fetchFromGitHub
, cmake
, pkg-config
, wrapQtAppsHook
, alsa-lib
, boost
, chromaprint
, fftw
, gnutls
, libcdio
, libebur128
, libmtp
, libpthreadstubs
, libtasn1
, libXdmcp
, ninja
, pcre
, protobuf
, sqlite
, taglib
, libgpod
, libidn2
, libpulseaudio
, libselinux
, libsepol
, p11-kit
, util-linux
, qtbase
, qtx11extras ? null # doesn't exist in qt6
, qttools
, withGstreamer ? true
, glib-networking
, gst_all_1
, withVlc ? true
, libvlc
, nix-update-script
{
alsa-lib,
boost,
chromaprint,
cmake,
fetchFromGitHub,
fftw,
glib-networking,
gnutls,
gst_all_1,
kdsingleapplication,
lib,
libXdmcp,
libcdio,
libebur128,
libgpod,
libidn2,
libmtp,
libpthreadstubs,
libpulseaudio,
libselinux,
libsepol,
libtasn1,
ninja,
nix-update-script,
p11-kit,
pkg-config,
qtbase,
qttools,
sqlite,
stdenv,
taglib,
util-linux,
wrapQtAppsHook,
}:
let
inherit (lib) optionals optionalString;
inherit (lib) optionals;
in
stdenv.mkDerivation rec {
pname = "strawberry";
version = "1.1.3";
version = "1.2.2";
src = fetchFromGitHub {
owner = "jonaski";
repo = pname;
rev = version;
hash = "sha256-yca1BJWhSUVamqSKfvEzU3xbzdR+kwfSs0pyS08oUR0=";
fetchSubmodules = true;
hash = "sha256-X752GsP2b7rumQHzw52zI7PeE8tdM9Scgl3nHVcpO/s=";
};
# the big strawberry shown in the context menu is *very* much in your face, so use the grey version instead
@@ -60,52 +55,55 @@ stdenv.mkDerivation rec {
--replace pictures/strawberry.png pictures/strawberry-grey.png
'';
buildInputs = [
alsa-lib
boost
chromaprint
fftw
gnutls
libcdio
libebur128
libidn2
libmtp
libpthreadstubs
libtasn1
libXdmcp
pcre
protobuf
sqlite
taglib
qtbase
qtx11extras
] ++ optionals stdenv.hostPlatform.isLinux [
libgpod
libpulseaudio
libselinux
libsepol
p11-kit
] ++ optionals withGstreamer (with gst_all_1; [
glib-networking
gstreamer
gst-libav
gst-plugins-base
gst-plugins-good
gst-plugins-bad
gst-plugins-ugly
]) ++ optionals withVlc [ libvlc ];
buildInputs =
[
alsa-lib
boost
chromaprint
fftw
gnutls
kdsingleapplication
libXdmcp
libcdio
libebur128
libidn2
libmtp
libpthreadstubs
libtasn1
qtbase
sqlite
taglib
]
++ optionals stdenv.hostPlatform.isLinux [
libgpod
libpulseaudio
libselinux
libsepol
p11-kit
]
++ (with gst_all_1; [
glib-networking
gst-libav
gst-plugins-bad
gst-plugins-base
gst-plugins-good
gst-plugins-ugly
gstreamer
]);
nativeBuildInputs = [
cmake
ninja
pkg-config
qttools
wrapQtAppsHook
] ++ optionals stdenv.hostPlatform.isLinux [
util-linux
];
nativeBuildInputs =
[
cmake
ninja
pkg-config
qttools
wrapQtAppsHook
]
++ optionals stdenv.hostPlatform.isLinux [
util-linux
];
postInstall = optionalString withGstreamer ''
postInstall = ''
qtWrapperArgs+=(
--prefix GST_PLUGIN_SYSTEM_PATH_1_0 : "$GST_PLUGIN_SYSTEM_PATH_1_0"
--prefix GIO_EXTRA_MODULES : "${glib-networking.out}/lib/gio/modules"
@@ -67,7 +67,7 @@ stdenv.mkDerivation (finalAttrs: {
postFixup = lib.optionalString (!stdenv.hostPlatform.isDarwin) ''
makeWrapper ${electron}/bin/electron $out/bin/youtube-music \
--add-flags $out/share/lib/youtube-music/resources/app.asar \
--add-flags "\''${NIXOS_OZONE_WL:+\''${WAYLAND_DISPLAY:+--ozone-platform-hint=auto --enable-features=WaylandWindowDecorations --enable-wayland-ime}}" \
--add-flags "\''${NIXOS_OZONE_WL:+\''${WAYLAND_DISPLAY:+--ozone-platform-hint=auto --enable-features=WaylandWindowDecorations --enable-wayland-ime=true}}" \
--set-default ELECTRON_FORCE_IS_PACKAGED 1 \
--set-default ELECTRON_IS_DEV 0 \
--inherit-argv0
@@ -78,8 +78,8 @@ stdenv.mkDerivation (finalAttrs: {
name = "youtube-music";
exec = "youtube-music %u";
icon = "youtube-music";
desktopName = "Youtube Music";
startupWMClass = "Youtube Music";
desktopName = "YouTube Music";
startupWMClass = "YouTube Music";
categories = [ "AudioVideo" ];
})
];

Some files were not shown because too many files have changed in this diff Show More