Merge master into staging-next

This commit is contained in:
nixpkgs-ci[bot]
2026-03-06 06:15:34 +00:00
committed by GitHub
150 changed files with 1851 additions and 1208 deletions
+12
View File
@@ -55,10 +55,14 @@ jobs:
})).map(file => file.filename)
if (files.some(file => [
'.github/workflows/build.yml',
'.github/workflows/check.yml',
'.github/workflows/eval.yml',
'.github/workflows/lint.yml',
'.github/workflows/merge-group.yml',
'.github/workflows/test.yml',
'ci/github-script/supportedSystems.js',
'ci/supportedBranches.js',
].includes(file))) core.setOutput('merge-group', true)
if (files.some(file => [
@@ -71,8 +75,16 @@ jobs:
'.github/workflows/pull-request-target.yml',
'.github/workflows/test.yml',
'ci/github-script/bot.js',
'ci/github-script/check-target-branch.js',
'ci/github-script/commits.js',
'ci/github-script/lint-commits.js',
'ci/github-script/merge.js',
'ci/github-script/prepare.js',
'ci/github-script/reviewers.js',
'ci/github-script/reviews.js',
'ci/github-script/supportedSystems.js',
'ci/github-script/withRateLimit.js',
'ci/supportedBranches.js',
].includes(file))) core.setOutput('pr', true)
merge-group:
+5 -6
View File
@@ -123,9 +123,7 @@ let
# - values: lists of `packagePlatformPath`s
diffAttrs = builtins.fromJSON (builtins.readFile "${combined}/combined-diff.json");
changedPackagePlatformAttrs = convertToPackagePlatformAttrs diffAttrs.changed;
rebuildsPackagePlatformAttrs = convertToPackagePlatformAttrs diffAttrs.rebuilds;
removedPackagePlatformAttrs = convertToPackagePlatformAttrs diffAttrs.removed;
changed-paths =
let
@@ -162,9 +160,10 @@ let
inherit
(callPackage ./maintainers.nix {
changedattrs = lib.attrNames (lib.groupBy (a: a.name) changedPackagePlatformAttrs);
changedpathsjson = touchedFilesJson;
removedattrs = lib.attrNames (lib.groupBy (a: a.name) removedPackagePlatformAttrs);
affectedAttrPaths = map (a: a.packagePath) (
convertToPackagePlatformAttrs (diffAttrs.changed ++ diffAttrs.removed)
);
changedFiles = lib.importJSON touchedFilesJson;
})
users
teams
@@ -181,7 +180,7 @@ runCommand "compare"
];
users = builtins.toJSON users;
teams = builtins.toJSON teams;
packages = builtins.toJSON packages;
packages = builtins.toJSON (lib.map (lib.concatStringsSep ".") packages);
passAsFile = [
"users"
"teams"
+86 -70
View File
@@ -1,70 +1,54 @@
# Figure out which maintainers (users/teams) are relevant for a PR:
# - All maintainers that can be linked directly to changedFiles
# - Maintainers of affectedAttrPaths if a file directly related to the attribute is in changedFiles
#
# Files and attributes are linked in various ways:
# - pkgs/by-name/<attr>/* is linked to pkgs.<attr>
# - The file position of various attributes of pkgs.<attr>
# - Explicitly specified file positions in derivations
#
# Test with
# nix-instantiate --eval --strict --json test.nix -A result | jq
#
# Empty list as an output means success
{
lib,
changedattrs,
changedpathsjson,
removedattrs,
}:
let
pkgs = import ../../.. {
# Files that were changed
# Type: ListOf (Nixpkgs-root-relative path)
changedFiles,
# Attributes whose value was affected by the change
# Type: ListOf (ListOf String)
affectedAttrPaths,
pkgs ? import ../../.. {
system = "x86_64-linux";
# We should never try to ping maintainers through package aliases, this can only lead to errors.
# One example case is, where an attribute is a throw alias, but then re-introduced in a PR.
# This would trigger the throw. By disabling aliases, we can fallback gracefully below.
config.allowAliases = false;
};
overlays = [ ];
},
lib,
}:
let
nixpkgsRoot = toString ../../.. + "/";
stripNixpkgsRootFromKeys = lib.mapAttrs' (
file: value: lib.nameValuePair (lib.removePrefix nixpkgsRoot file) value
);
changedpaths = lib.importJSON changedpathsjson;
moduleMeta = (pkgs.nixos { }).config.meta;
# Extract attributes that changed from by-name paths.
# This allows pinging reviewers for pure refactors.
touchedattrs = lib.pipe changedpaths [
(lib.filter (changed: lib.hasPrefix "pkgs/by-name/" changed && changed != "pkgs/by-name/README.md"))
(map (lib.splitString "/"))
(map (path: lib.elemAt path 3))
lib.unique
];
# Currently just nixos module maintainers, but in the future we can use this for code owners too
fileUsers = stripNixpkgsRootFromKeys moduleMeta.maintainers;
fileTeams = stripNixpkgsRootFromKeys moduleMeta.teams;
anyMatchingFile = filename: lib.any (lib.hasPrefix filename) changedpaths;
anyMatchingFile = filename: lib.any (lib.hasPrefix filename) changedFiles;
anyMatchingFiles = files: lib.any anyMatchingFile files;
sharded = name: "${lib.substring 0 2 name}/${name}";
attrsWithMaintainers = lib.pipe (changedattrs ++ removedattrs ++ touchedattrs) [
# An attribute can appear in changed/removed *and* touched
lib.unique
(map (
name:
let
path = lib.splitString "." name;
# Some packages might be reported as changed on a different platform, but
# not even have an attribute on the platform the maintainers are requested on.
# Fallback to `null` for these to filter them out below.
package = lib.attrByPath path null pkgs;
in
{
inherit name package;
# Adds all files in by-name to each package, no matter whether they are discoverable
# via meta attributes below. For example, this allows pinging maintainers for
# updates to .json files.
# TODO: Support by-name package sets.
filenames = lib.optional (lib.length path == 1) "pkgs/by-name/${sharded (lib.head path)}/";
# meta.maintainers also contains all individual team members.
# We only want to ping individuals if they're added individually as maintainers, not via teams.
users = package.meta.nonTeamMaintainers or [ ];
teams = package.meta.teams or [ ];
}
))
# No need to match up packages without maintainers with their files.
# This also filters out attributes where `package = null`, which is the
# case for libintl, for example.
(lib.filter (pkg: pkg.users != [ ] || pkg.teams != [ ]))
];
relevantFilenames =
drv:
(lib.unique (
map (pos: lib.removePrefix "${toString ../../..}/" pos.file) (
map (pos: lib.removePrefix nixpkgsRoot pos.file) (
lib.filter (x: x != null) [
(drv.meta.maintainersPosition or null)
(drv.meta.teamsPosition or null)
@@ -87,50 +71,82 @@ let
)
));
attrsWithFilenames = map (
pkg: pkg // { filenames = pkg.filenames ++ relevantFilenames pkg.package; }
) attrsWithMaintainers;
relevantAffectedAttrPaths = lib.filter (
attrPath:
# Some packages might be reported as changed on a different platform, but
# not even have an attribute on the platform the maintainers are requested on.
# Fallback to `null` for these to filter them out
let
package = lib.attrByPath attrPath null pkgs;
in
package != null && anyMatchingFiles (relevantFilenames package)
) affectedAttrPaths;
attrsWithModifiedFiles = lib.filter (pkg: anyMatchingFiles pkg.filenames) attrsWithFilenames;
# Extract attributes that changed from by-name paths.
# This allows pinging reviewers for pure refactors.
changedByNameAttrPaths = lib.pipe changedFiles [
(lib.filter (changed: lib.hasPrefix "pkgs/by-name/" changed))
(map (lib.splitString "/"))
# Filters out e.g. pkgs/by-name/README.md
(lib.filter (path: lib.length path > 3))
(map (path: lib.elemAt path 3))
(map lib.singleton)
];
# An attribute can appear in affected *and* touched
attrPathsToGetMaintainersFor = lib.unique (relevantAffectedAttrPaths ++ changedByNameAttrPaths);
attrPathEntities = lib.concatMap (
attrPath:
let
package = lib.getAttrFromPath attrPath pkgs;
in
# meta.maintainers also contains all individual team members.
# We only want to ping individuals if they're added individually as maintainers, not via teams.
userPings { inherit attrPath; } (package.meta.nonTeamMaintainers or [ ])
++ lib.concatMap (teamPings { inherit attrPath; }) (package.meta.teams or [ ])
) attrPathsToGetMaintainersFor;
changedFileEntities = lib.concatMap (
file:
userPings { inherit file; } (fileUsers.${file} or [ ])
++ lib.concatMap (teamPings { inherit file; }) (fileTeams.${file} or [ ])
) changedFiles;
userPings =
pkg:
context:
map (maintainer: {
type = "user";
userId = maintainer.githubId;
packageName = pkg.name;
inherit context;
});
teamPings =
pkg: team:
if team ? github then
context: team:
if team ? githubId then
[
{
type = "team";
teamId = team.githubId;
packageName = pkg.name;
inherit context;
}
]
else
userPings pkg team.members;
userPings context team.members;
maintainersToPing = lib.concatMap (
pkg: userPings pkg pkg.users ++ lib.concatMap (teamPings pkg) pkg.teams
) attrsWithModifiedFiles;
byType = lib.groupBy (ping: ping.type) maintainersToPing;
byType = lib.groupBy (ping: ping.type) (attrPathEntities ++ changedFileEntities);
byUser = lib.pipe (byType.user or [ ]) [
(lib.groupBy (ping: toString ping.userId))
(lib.mapAttrs (_user: lib.map (pkg: pkg.packageName)))
(lib.mapAttrs (_user: lib.map (pkg: pkg.context)))
];
byTeam = lib.pipe (byType.team or [ ]) [
(lib.groupBy (ping: toString ping.teamId))
(lib.mapAttrs (_team: lib.map (pkg: pkg.packageName)))
(lib.mapAttrs (_team: lib.map (pkg: pkg.context)))
];
in
{
users = byUser;
teams = byTeam;
packages = lib.catAttrs "name" attrsWithModifiedFiles;
packages = attrPathsToGetMaintainersFor;
}
+228
View File
@@ -0,0 +1,228 @@
{
pkgs ? import ../../.. {
config = { };
overlays = [ ];
},
lib ? pkgs.lib,
}:
let
fun = import ./maintainers.nix;
mockPkgs =
{
packages ? [ ],
modules ? [ ],
githubTeams ? true,
}:
lib.updateManyAttrsByPath
(lib.imap0 (i: p: {
path = p;
update = _: {
meta.maintainersPosition.file = lib.concatStringsSep "/" p;
meta.nonTeamMaintainers = [ { githubId = i; } ];
meta.teams =
if githubTeams then [ { githubId = i + 100; } ] else [ { members = [ { githubId = i + 100; } ]; } ];
};
}) packages)
{
nixos =
{ }:
{
config.meta.maintainers = lib.listToAttrs (
lib.imap0 (i: m: lib.nameValuePair m [ { githubId = i; } ]) modules
);
config.meta.teams = lib.listToAttrs (
lib.imap0 (
i: m:
lib.nameValuePair m (
if githubTeams then [ { githubId = i + 100; } ] else [ { members = [ { githubId = i + 100; } ]; } ]
)
) modules
);
};
};
tests = {
testEmpty = {
expr = fun {
pkgs = mockPkgs { };
inherit lib;
changedFiles = [ ];
affectedAttrPaths = [ ];
};
expected = {
packages = [ ];
teams = { };
users = { };
};
};
testNonExistentAffected = {
expr = fun {
pkgs = mockPkgs { };
inherit lib;
changedFiles = [ "a" ];
affectedAttrPaths = [ [ "b" ] ];
};
expected = {
packages = [ ];
teams = { };
users = { };
};
};
testIrrelevantAffected = {
expr = fun {
pkgs = mockPkgs {
packages = [ [ "b" ] ];
};
inherit lib;
changedFiles = [ "a" ];
affectedAttrPaths = [ [ "b" ] ];
};
expected = {
packages = [ ];
teams = { };
users = { };
};
};
testRelevantAffected = {
expr = fun {
pkgs = mockPkgs {
packages = [ [ "b" ] ];
};
inherit lib;
# Also tests that subpaths work
changedFiles = [ "b/c" ];
affectedAttrPaths = [ [ "b" ] ];
};
expected = {
packages = [ [ "b" ] ];
teams."100" = [
{ attrPath = [ "b" ]; }
];
users."0" = [
{ attrPath = [ "b" ]; }
];
};
};
testRelevantAffectedNonGitHub = {
expr = fun {
pkgs = mockPkgs {
packages = [ [ "b" ] ];
githubTeams = false;
};
inherit lib;
changedFiles = [ "b/c" ];
affectedAttrPaths = [ [ "b" ] ];
};
expected = {
packages = [ [ "b" ] ];
teams = { };
users."0" = [
{ attrPath = [ "b" ]; }
];
users."100" = [
{ attrPath = [ "b" ]; }
];
};
};
testByNameChanged = {
expr = fun {
pkgs = mockPkgs {
packages = [ [ "hello" ] ];
};
inherit lib;
changedFiles = [ "pkgs/by-name/he/hello/sources.json" ];
affectedAttrPaths = [ ];
};
expected = {
packages = [ [ "hello" ] ];
teams."100" = [
{ attrPath = [ "hello" ]; }
];
users."0" = [
{ attrPath = [ "hello" ]; }
];
};
};
testByNameReadmeChanged = {
expr = fun {
pkgs = mockPkgs {
packages = [ [ "hello" ] ];
};
inherit lib;
changedFiles = [ "pkgs/by-name/README.md" ];
affectedAttrPaths = [ ];
};
expected = {
packages = [ ];
teams = { };
users = { };
};
};
testNoDuplicates = {
expr = fun {
pkgs = mockPkgs {
packages = [ [ "hello" ] ];
};
inherit lib;
changedFiles = [
"hello"
"pkgs/by-name/he/hello/sources.json"
];
affectedAttrPaths = [ [ "hello" ] ];
};
expected = {
packages = [ [ "hello" ] ];
teams."100" = [
{ attrPath = [ "hello" ]; }
];
users."0" = [
{ attrPath = [ "hello" ]; }
];
};
};
testModuleMaintainers = {
expr = fun {
pkgs = mockPkgs {
modules = [ "a" ];
};
inherit lib;
changedFiles = [ "a" ];
affectedAttrPaths = [ ];
};
expected = {
packages = [ ];
teams."100" = [
{ file = "a"; }
];
users."0" = [
{ file = "a"; }
];
};
};
testModuleMaintainersNonGithub = {
expr = fun {
pkgs = mockPkgs {
modules = [ "a" ];
githubTeams = false;
};
inherit lib;
changedFiles = [ "a" ];
affectedAttrPaths = [ ];
};
expected = {
packages = [ ];
teams = { };
users."100" = [
{ file = "a"; }
];
users."0" = [
{ file = "a"; }
];
};
};
};
in
{
result = lib.runTests tests;
}
+27 -31
View File
@@ -4,39 +4,12 @@
let
inherit (lib)
mkOption
mkOptionType
types
;
maintainer = mkOptionType {
name = "maintainer";
check = email: lib.elem email (lib.attrValues lib.maintainers);
merge = loc: defs: {
# lib.last: Perhaps this could be merged instead, if "at most once per module"
# is a problem (see option description).
${(lib.last defs).file} = (lib.last defs).value;
};
};
listOfMaintainers = types.listOf maintainer // {
merge =
loc: defs:
lib.zipAttrs (
lib.flatten (
lib.imap1 (
n: def:
lib.imap1 (
m: def':
maintainer.merge (loc ++ [ "[${toString n}-${toString m}]" ]) [
{
inherit (def) file;
value = def';
}
]
) def.value
) defs
)
);
# The resulting value of this type shows where all values were defined
sourceList = types.listOf types.raw // {
merge = loc: defs: lib.listToAttrs (lib.map ({ file, value }: lib.nameValuePair file value) defs);
};
in
{
@@ -44,7 +17,14 @@ in
options = {
meta = {
maintainers = mkOption {
type = listOfMaintainers;
type =
let
allMaintainers = lib.attrValues lib.maintainers;
in
lib.types.addCheck sourceList (lib.all (v: lib.elem v allMaintainers))
// {
description = "list of lib.maintainers";
};
default = [ ];
example = lib.literalExpression "[ lib.maintainers.alice lib.maintainers.bob ]";
description = ''
@@ -54,6 +34,22 @@ in
The option value is not a list of maintainers, but an attribute set that maps module file names to lists of maintainers.
'';
};
teams = mkOption {
type =
let
allTeams = lib.attrValues lib.teams;
in
lib.types.addCheck sourceList (lib.all (v: lib.elem v allTeams))
// {
description = "list of lib.teams";
};
default = [ ];
example = lib.literalExpression "[ lib.teams.acme lib.teams.haskell ]";
description = ''
List of team maintainers of each module.
This option should be defined at most once per module.
'';
};
};
};
meta.maintainers = with lib.maintainers; [
+9 -1
View File
@@ -14,9 +14,17 @@ let
};
in
rec {
lib = import ../../../lib;
# Inject ghost into lib.maintainers so it passes the addCheck validation
lib = (import ../../../lib).extend (
final: prev: {
maintainers = prev.maintainers // {
inherit ghost;
};
}
);
example = lib.evalModules {
specialArgs.lib = lib;
modules = [
../meta-maintainers.nix
{
+1 -1
View File
@@ -22,7 +22,7 @@ in
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
options = {
+1 -1
View File
@@ -1,7 +1,7 @@
{ config, lib, ... }:
{
meta = {
maintainers = lib.teams.freedesktop.members;
teams = [ lib.teams.freedesktop ];
};
options = {
+1 -1
View File
@@ -6,7 +6,7 @@
}:
{
meta = {
maintainers = lib.teams.freedesktop.members;
teams = [ lib.teams.freedesktop ];
};
options = {
+1 -1
View File
@@ -1,7 +1,7 @@
{ config, lib, ... }:
{
meta = {
maintainers = lib.teams.freedesktop.members;
teams = [ lib.teams.freedesktop ];
};
options = {
+1 -1
View File
@@ -13,7 +13,7 @@ in
{
meta = {
maintainers = lib.teams.freedesktop.members ++ [ ];
teams = [ lib.teams.freedesktop ];
};
options = {
+1 -1
View File
@@ -32,7 +32,7 @@ in
];
meta = {
maintainers = teams.freedesktop.members;
teams = [ teams.freedesktop ];
};
options.xdg.portal = {
+1 -1
View File
@@ -10,7 +10,7 @@ let
in
{
meta = {
maintainers = lib.teams.lxqt.members;
teams = [ lib.teams.lxqt ];
};
options.xdg.portal.lxqt = {
+1 -1
View File
@@ -6,7 +6,7 @@
}:
{
meta = {
maintainers = lib.teams.freedesktop.members;
teams = [ lib.teams.freedesktop ];
};
options = {
+1
View File
@@ -1758,6 +1758,7 @@
./services/web-apps/snipe-it.nix
./services/web-apps/snips-sh.nix
./services/web-apps/sogo.nix
./services/web-apps/speedtest-tracker.nix
./services/web-apps/sshwifty.nix
./services/web-apps/stash.nix
./services/web-apps/stirling-pdf.nix
+1 -1
View File
@@ -49,5 +49,5 @@ in
systemd.user.services.dsearch.wantedBy = mkIf cfg.systemd.enable [ cfg.systemd.target ];
};
meta.maintainers = lib.teams.danklinux.members;
meta.teams = [ lib.teams.danklinux ];
}
+1 -1
View File
@@ -11,7 +11,7 @@ let
in
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
options = {
+1 -1
View File
@@ -10,7 +10,7 @@
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
###### interface
+1 -1
View File
@@ -16,7 +16,7 @@ in
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
options = {
+1 -1
View File
@@ -10,7 +10,7 @@ let
in
{
meta = {
maintainers = lib.teams.freedesktop.members;
teams = [ lib.teams.freedesktop ];
};
options.programs.nm-applet = {
+1 -1
View File
@@ -285,5 +285,5 @@ in
];
};
meta.maintainers = lib.teams.steam.members;
meta.teams = [ lib.teams.steam ];
}
+1 -1
View File
@@ -11,7 +11,7 @@ let
in
{
meta = {
maintainers = lib.teams.xfce.members;
teams = [ lib.teams.xfce ];
};
options = {
+1 -1
View File
@@ -226,5 +226,5 @@ in
hardware.graphics.enable = lib.mkDefault true;
};
meta.maintainers = lib.teams.danklinux.members;
meta.teams = [ lib.teams.danklinux ];
}
+1 -1
View File
@@ -130,5 +130,5 @@ in
] "Nvidia patches are no longer needed")
];
meta.maintainers = lib.teams.hyprland.members;
meta.teams = [ lib.teams.hyprland ];
}
+1 -1
View File
@@ -26,5 +26,5 @@ in
security.pam.services.hyprlock = { };
};
meta.maintainers = lib.teams.hyprland.members;
meta.teams = [ lib.teams.hyprland ];
}
+1 -1
View File
@@ -11,7 +11,7 @@ let
in
{
meta = {
maintainers = lib.teams.xfce.members;
teams = [ lib.teams.xfce ];
};
options = {
+1 -1
View File
@@ -1218,7 +1218,7 @@ in
];
meta = {
maintainers = lib.teams.acme.members;
teams = [ lib.teams.acme ];
doc = ./default.md;
};
}
+1 -1
View File
@@ -272,5 +272,5 @@ in
};
};
meta.maintainers = lib.teams.apparmor.members;
meta.teams = [ lib.teams.apparmor ];
}
@@ -962,5 +962,6 @@ in
(import ./rke2.nix args)
];
meta.maintainers = pkgs.rke2.meta.maintainers ++ lib.teams.k3s.members;
meta.teams = [ lib.teams.k3s ];
meta.maintainers = pkgs.rke2.meta.maintainers;
}
@@ -313,5 +313,5 @@ in
'')
];
meta.maintainers = lib.teams.buildbot.members;
meta.teams = [ lib.teams.buildbot ];
}
@@ -196,6 +196,6 @@ in
};
};
meta.maintainers = lib.teams.buildbot.members;
meta.teams = [ lib.teams.buildbot ];
}
@@ -893,5 +893,5 @@ in
)
];
meta.maintainers = teams.gitlab.members;
meta.teams = [ teams.gitlab ];
}
@@ -13,7 +13,7 @@ let
in
{
meta.maintainers = [ "thevar1able" ];
meta.maintainers = with lib.maintainers; [ thevar1able ];
###### interface
+148 -13
View File
@@ -335,7 +335,23 @@ in
If the master is password protected (using the requirePass configuration)
it is possible to tell the slave to authenticate before starting the replication synchronization
process, otherwise the master will refuse the slave request.
(STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)'';
(STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)
'';
};
masterAuthFile = lib.mkOption {
type = with types; nullOr path;
default = null;
description = "File with password for the master user.";
example = "/run/keys/redis-master-password";
};
masterUser = lib.mkOption {
type = with types; nullOr str;
default = null;
description = ''
If the master is password protected via ACLs this option can be used to specify
the Redis user that is used by replicas.'';
};
requirePass = lib.mkOption {
@@ -355,6 +371,43 @@ in
example = "/run/keys/redis-password";
};
sentinelAuthPassFile = lib.mkOption {
type = with types; nullOr path;
default = null;
description = "File with password for connecting to other Sentinel instances.";
example = "/run/keys/sentinel-password";
};
sentinelAuthUser = lib.mkOption {
type = with types; nullOr str;
default = null;
description = "The username to use to monitor a master from Sentinel.";
};
sentinelMasterHost = lib.mkOption {
type = with types; nullOr str;
default = null;
description = "The IP address (recommended) or hostname of the Redis master that Sentinel will monitor.";
};
sentinelMasterName = lib.mkOption {
type = with types; nullOr str;
default = null;
description = "The master name of the Redis master that Sentinel will monitor.";
};
sentinelMasterPort = lib.mkOption {
type = with types; nullOr int;
default = null;
description = "The TCP port of the Redis master that Sentinel will monitor.";
};
sentinelMasterQuorum = lib.mkOption {
type = with types; nullOr int;
default = null;
description = "The Sentinel quorum (minimum number of Sentinel nodes online for failover)";
};
appendOnly = lib.mkOption {
type = types.bool;
default = false;
@@ -452,14 +505,55 @@ in
config = lib.mkIf (enabledServers != { }) {
assertions = lib.attrValues (
lib.mapAttrs (name: conf: {
assertion = conf.requirePass != null -> conf.requirePassFile == null;
message = ''
You can only set one services.redis.servers.${name}.requirePass
or services.redis.servers.${name}.requirePassFile
'';
}) enabledServers
assertions = lib.concatLists (
lib.mapAttrsToList (name: conf: [
{
assertion = conf.requirePass != null -> conf.requirePassFile == null;
message = ''
You can only set one of services.redis.servers.${name}.requirePass
or services.redis.servers.${name}.requirePassFile
'';
}
{
assertion = conf.masterAuth != null -> conf.masterAuthFile == null;
message = ''
You can only set one of services.redis.servers.${name}.masterAuth
or services.redis.servers.${name}.masterAuthFile
'';
}
{
assertion = conf.masterUser != null -> (conf.masterAuth != null || conf.masterAuthFile != null);
message = ''
If using services.redis.servers.${name}.masterUser, either
services.redis.servers.${name}.masterAuthFile or
services.redis.servers.${name}.masterAuth must be provided
'';
}
{
assertion =
conf.sentinelMasterName != null
-> (
conf.sentinelMasterHost != null
&& conf.sentinelMasterPort != null
&& conf.sentinelMasterQuorum != null
);
message = ''
For Sentinel,
services.redis.servers.${name}.sentinelMasterName,
services.redis.servers.${name}.sentinelMasterHost,
services.redis.servers.${name}.sentinelMasterPort,
and services.redis.servers.${name}.sentinelMasterQuorum
must all be provided
'';
}
{
assertion = conf.sentinelAuthPassFile != null -> conf.sentinelMasterName != null;
message = ''
For Sentinel authentication, services.redis.servers.${name}.sentinelMasterName,
must be provided
'';
}
]) enabledServers
);
boot.kernel.sysctl = lib.mkIf cfg.vmOverCommit {
@@ -498,6 +592,17 @@ in
ExecStart = "${cfg.package}/bin/${
cfg.package.serverBin or "redis-server"
} /var/lib/${redisName name}/redis.conf ${lib.escapeShellArgs conf.extraParams}";
# NOTE: Redis/Valkey Sentinel persists dynamic cluster state by rewriting its
# configuration file at runtime (redis.conf). This includes monitors,
# authentication credentials, and failover metadata, and this behaviour
# cannot be disabled.
# As a result, a fully declarative configuration is not possible for
# Sentinel-managed options. The preStart logic below appends sentinel
# configuration only if it is not already present, in order to avoid
# overwriting state that is owned and maintained by Sentinel itself.
# This is an intentional deviation from strict declarative semantics and
# is required for correct Sentinel operation.
ExecStartPre =
"+"
+ pkgs.writeShellScript "${redisName name}-prep-conf" (
@@ -515,10 +620,40 @@ in
fi
echo 'include "${redisConfStore}"' > "${redisConfRun}"
${lib.optionalString (conf.requirePassFile != null) ''
{
echo -n "requirepass "
cat ${lib.escapeShellArg conf.requirePassFile}
} >> "${redisConfRun}"
echo "requirepass $(cat ${lib.escapeShellArg conf.requirePassFile})" >> "${redisConfRun}"
''}
${lib.optionalString (conf.masterUser != null) ''
echo "masteruser ${conf.masterUser}" >> "${redisConfRun}"
''}
${lib.optionalString (conf.masterAuthFile != null) ''
echo "masterauth $(cat ${lib.escapeShellArg conf.masterAuthFile})" >> "${redisConfRun}"
''}
${lib.optionalString (conf.sentinelMasterHost != null) ''
sentinel_monitor_line="sentinel monitor ${conf.sentinelMasterName} ${conf.sentinelMasterHost} ${toString conf.sentinelMasterPort} ${toString conf.sentinelMasterQuorum}"
if grep -qE "^sentinel monitor ${conf.sentinelMasterName}\b" "${redisConfVar}"; then
sed -i \
"s|^sentinel monitor ${conf.sentinelMasterName}\b.*|$sentinel_monitor_line|" "${redisConfVar}"
else
echo "$sentinel_monitor_line" >> "${redisConfVar}"
fi
''}
${lib.optionalString (conf.sentinelAuthUser != null) ''
sentinel_auth_user_line="sentinel auth-user ${conf.sentinelMasterName} ${conf.sentinelAuthUser}"
if grep -qE "^sentinel auth-user ${conf.sentinelMasterName}\b" "${redisConfVar}"; then
sed -i \
"s|^sentinel auth-user ${conf.sentinelMasterName}\b.*|$sentinel_auth_user_line|" "${redisConfVar}"
else
echo "$sentinel_auth_user_line" >> "${redisConfVar}"
fi
''}
${lib.optionalString (conf.sentinelAuthPassFile != null) ''
sentinel_auth_pass_line="sentinel auth-pass ${conf.sentinelMasterName} $(cat ${lib.escapeShellArg conf.sentinelAuthPassFile})"
if grep -qE "^sentinel auth-pass ${conf.sentinelMasterName}\b" "${redisConfVar}"; then
sed -i \
"s|^sentinel auth-pass ${conf.sentinelMasterName}\b.*|$sentinel_auth_pass_line|" "${redisConfVar}"
else
echo "$sentinel_auth_pass_line" >> "${redisConfVar}"
fi
''}
''
);
@@ -61,7 +61,7 @@ let
notExcluded = pkg: utils.disablePackageByName pkg config.environment.budgie.excludePackages;
in
{
meta.maintainers = lib.teams.budgie.members;
meta.teams = [ lib.teams.budgie ];
imports = [
(lib.mkRenamedOptionModule
@@ -44,7 +44,7 @@ let
];
in
{
meta.maintainers = lib.teams.cosmic.members;
meta.teams = [ lib.teams.cosmic ];
options = {
services.desktopManager.cosmic = {
@@ -82,7 +82,7 @@ in
{
meta = {
doc = ./gnome.md;
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
imports = [
@@ -292,5 +292,5 @@ in
})
];
meta.maintainers = lib.teams.lomiri.members;
meta.teams = [ lib.teams.lomiri ];
}
@@ -25,7 +25,7 @@ in
meta = {
doc = ./pantheon.md;
maintainers = teams.pantheon.members;
teams = [ teams.pantheon ];
};
imports = [
@@ -7,7 +7,7 @@
}:
{
meta = {
maintainers = lib.teams.freedesktop.members;
teams = [ lib.teams.freedesktop ];
};
###### interface
+1 -1
View File
@@ -373,6 +373,6 @@ in
};
meta = {
maintainers = [ ] ++ lib.teams.pantheon.members;
teams = [ lib.teams.pantheon ];
};
}
@@ -10,7 +10,7 @@
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
###### interface
@@ -10,7 +10,7 @@
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
###### interface
@@ -13,7 +13,7 @@ let
in
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
options = {
@@ -10,7 +10,7 @@
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
###### interface
@@ -16,7 +16,7 @@ in
{
meta = {
maintainers = teams.gnome.members;
teams = [ teams.gnome ];
};
options = {
@@ -48,7 +48,7 @@ in
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
###### interface
@@ -12,7 +12,7 @@ in
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
options = {
@@ -10,7 +10,7 @@
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
###### interface
@@ -8,7 +8,7 @@
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
###### interface
@@ -16,7 +16,7 @@ in
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
###### interface
@@ -7,7 +7,7 @@
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
options = {
@@ -10,7 +10,7 @@
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
###### interface
@@ -7,7 +7,7 @@
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
imports = [
@@ -14,7 +14,7 @@ in
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
###### interface
@@ -10,7 +10,7 @@
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
###### interface
@@ -10,7 +10,7 @@ let
in
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
imports = [
+1 -1
View File
@@ -16,7 +16,7 @@ in
{
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
###### interface
@@ -85,7 +85,8 @@ let
};
in
{
meta.maintainers = teams.freedesktop.members ++ [ maintainers.k900 ];
meta.teams = [ teams.freedesktop ];
meta.maintainers = [ maintainers.k900 ];
###### interface
options = {
+1 -1
View File
@@ -18,7 +18,7 @@ in
];
meta = {
maintainers = [ ] ++ lib.teams.pantheon.members;
teams = [ lib.teams.pantheon ];
};
###### interface
@@ -8,7 +8,7 @@
{
meta = {
maintainers = [ ] ++ lib.teams.pantheon.members;
teams = [ lib.teams.pantheon ];
};
###### interface
@@ -16,7 +16,7 @@ let
in
{
meta.maintainers = lib.teams.cosmic.members;
meta.teams = [ lib.teams.cosmic ];
options.services.displayManager.cosmic-greeter = {
enable = lib.mkEnableOption "COSMIC greeter";
@@ -321,5 +321,5 @@ in
services.libinput.enable = mkDefault true;
};
meta.maintainers = lib.teams.danklinux.members;
meta.teams = [ lib.teams.danklinux ];
}
@@ -105,7 +105,7 @@ in
];
meta = {
maintainers = lib.teams.gnome.members;
teams = [ lib.teams.gnome ];
};
###### interface
@@ -171,7 +171,7 @@ in
meta = {
buildDocsInSandbox = false;
maintainers = lib.teams.home-assistant.members;
teams = [ lib.teams.home-assistant ];
};
options.services.home-assistant = {
+1 -1
View File
@@ -341,5 +341,5 @@ in
};
};
};
meta.maintainers = lib.teams.matrix.members;
meta.teams = [ lib.teams.matrix ];
}
+1 -1
View File
@@ -849,5 +849,5 @@ in
};
meta.doc = ./forgejo.md;
meta.maintainers = lib.teams.forgejo.members;
meta.teams = [ lib.teams.forgejo ];
}
+1 -1
View File
@@ -1911,5 +1911,5 @@ in
};
meta.doc = ./gitlab.md;
meta.maintainers = teams.gitlab.members;
meta.teams = [ teams.gitlab ];
}
+1 -1
View File
@@ -63,5 +63,5 @@ in
};
};
meta.maintainers = lib.teams.beam.members;
meta.teams = [ lib.teams.beam ];
}
@@ -444,5 +444,5 @@ in
};
};
meta.maintainers = lib.teams.jitsi.members;
meta.teams = [ lib.teams.jitsi ];
}
+1 -1
View File
@@ -166,5 +166,5 @@ in
lib.mkDefault "${pkgs.jicofo}/etc/jitsi/jicofo/logging.properties-journal";
};
meta.maintainers = lib.teams.jitsi.members;
meta.teams = [ lib.teams.jitsi ];
}
+1 -1
View File
@@ -243,5 +243,5 @@ in
lib.mkDefault "${stateDir}/logging.properties-journal";
};
meta.maintainers = lib.teams.jitsi.members;
meta.teams = [ lib.teams.jitsi ];
}
@@ -331,5 +331,5 @@ in
];
};
meta.maintainers = lib.teams.jitsi.members;
meta.teams = [ lib.teams.jitsi ];
}
@@ -9,7 +9,7 @@ let
in
{
meta = {
maintainers = lib.teams.freedesktop.members;
teams = [ lib.teams.freedesktop ];
};
options = with lib; {
@@ -142,9 +142,8 @@ in
{
meta = {
maintainers = teams.freedesktop.members ++ [
lib.maintainers.frontear
];
teams = [ lib.teams.freedesktop ];
maintainers = [ lib.maintainers.frontear ];
};
###### interface
@@ -29,8 +29,15 @@ let
configFile = pkgs.writeText "shadowsocks.json" (builtins.toJSON opts);
executablesMap = {
"${getName pkgs.shadowsocks-libev}" = {
server = "ss-server";
};
"${getName pkgs.shadowsocks-rust}" = {
server = "ssserver";
};
};
in
{
###### interface
@@ -47,14 +54,25 @@ in
'';
};
package = mkPackageOption pkgs "Shadowsocks" {
default = "shadowsocks-libev";
};
localAddress = mkOption {
type = types.coercedTo types.str singleton (types.listOf types.str);
type =
with types;
oneOf [
str
(listOf str)
];
# Keeped for compatibility
default = [
"[::0]"
"0.0.0.0"
];
description = ''
Local addresses to which the server binds.
Note: shadowsocks-rust accepts only string parameter.
'';
};
@@ -163,14 +181,19 @@ in
(noPasswd && !noPasswdFile) || (!noPasswd && noPasswdFile);
message = "Option `password` or `passwordFile` must be set and cannot be set simultaneously";
}
{
# Ensure localAddress is a string if package is shadowsocks-rust
assertion = !(getName cfg.package == "shadowsocks-rust" && !lib.strings.isString cfg.localAddress);
message = "Option `localAddress` must be a string when using shadowsocks-rust.";
}
];
systemd.services.shadowsocks-libev = {
description = "shadowsocks-libev Daemon";
systemd.services.${getName cfg.package} = {
description = "${getName cfg.package} Daemon";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = [
pkgs.shadowsocks-libev
cfg.package
]
++ optional (cfg.plugin != null) cfg.plugin
++ optional (cfg.passwordFile != null) pkgs.jq;
@@ -179,7 +202,9 @@ in
${optionalString (cfg.passwordFile != null) ''
cat ${configFile} | jq --arg password "$(cat "${cfg.passwordFile}")" '. + { password: $password }' > /tmp/shadowsocks.json
''}
exec ss-server -c ${if cfg.passwordFile != null then "/tmp/shadowsocks.json" else configFile}
exec ${(executablesMap.${getName cfg.package}).server} -c ${
if cfg.passwordFile != null then "/tmp/shadowsocks.json" else configFile
}
'';
};
};
@@ -121,6 +121,9 @@ in
Path to file which contains the master key.
By doing so, all routes will be protected and will require a key to be accessed.
If no master key is provided, all routes can be accessed without requiring any key.
You can generate a master key by running `openssl rand -base64 36`.
Alternatively, you can start Meilisearch without a master key and use the pre-generated key from the service's logs that can be obtained by `journalctl -u meilisearch | grep -- --master-key`.
'';
default = null;
type = lib.types.nullOr lib.types.path;
+4 -6
View File
@@ -299,10 +299,8 @@ in
environment.systemPackages = [ cfg.package ];
};
meta.maintainers =
with lib.maintainers;
[
ppom
]
++ lib.teams.ngi.members;
meta.teams = [ lib.teams.ngi ];
meta.maintainers = with lib.maintainers; [
ppom
];
}
+1 -1
View File
@@ -28,5 +28,5 @@ in
};
};
meta.maintainers = lib.teams.hyprland.members;
meta.teams = [ lib.teams.hyprland ];
}
@@ -756,5 +756,5 @@ in
};
meta.doc = ./jitsi-meet.md;
meta.maintainers = lib.teams.jitsi.members;
meta.teams = [ lib.teams.jitsi ];
}
+34 -1
View File
@@ -6,6 +6,7 @@
}:
let
cfg = config.services.librechat;
meiliCfg = config.services.meilisearch;
format = pkgs.formats.yaml { };
configFile = format.generate "librechat.yaml" cfg.settings;
exportCredentials = n: _: ''export ${n}="$(${pkgs.systemd}/bin/systemd-creds cat ${n}_FILE)"'';
@@ -155,6 +156,26 @@ in
};
enableLocalDB = lib.mkEnableOption "a local mongodb instance";
meilisearch = lib.mkOption {
type = lib.types.submodule {
options = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
example = true;
description = ''
Whether to enable and configure Meilisearch locally for Librechat.
You will manually need to set `services.meilisearch.masterKeyFile`.
'';
};
};
};
default = { };
description = ''
See [LibreChat search feature](https://www.librechat.ai/docs/features/search).
'';
};
};
config = lib.mkIf cfg.enable {
@@ -178,6 +199,12 @@ in
You can use https://www.librechat.ai/toolkit/creds_generator to generate these.
'';
}
{
assertion = cfg.meilisearch.enable -> meiliCfg.masterKeyFile != null;
message = ''
LibreChat's Meilisearch integration requires `services.meilisearch.masterKeyFile` to be set.
'';
}
];
networking.firewall.allowedTCPPorts = lib.optional cfg.openFirewall cfg.port;
@@ -192,7 +219,8 @@ in
after = [
"tmpfiles.target"
]
++ lib.optional cfg.enableLocalDB "mongodb.service";
++ lib.optional cfg.meilisearch.enable "meilisearch.service";
wants = lib.optional cfg.meilisearch.enable "meilisearch.service";
description = "Open-source app for all your AI conversations, fully customizable and compatible with any AI provider";
environment = cfg.env;
script = # sh
@@ -249,6 +277,11 @@ in
services.librechat.env.MONGO_URI = lib.mkIf cfg.enableLocalDB "mongodb://localhost:27017";
services.mongodb.enable = lib.mkIf cfg.enableLocalDB true;
services.meilisearch.enable = lib.mkIf cfg.meilisearch.enable true;
services.librechat.env.SEARCH = lib.mkIf cfg.meilisearch.enable true;
services.librechat.env.MEILI_HOST = lib.mkIf cfg.meilisearch.enable "http://${meiliCfg.settings.http_addr}";
services.librechat.credentials.MEILI_MASTER_KEY = lib.mkIf cfg.meilisearch.enable meiliCfg.masterKeyFile;
};
meta.maintainers = with lib.maintainers; [
@@ -1774,5 +1774,5 @@ in
);
meta.doc = ./nextcloud.md;
meta.maintainers = lib.teams.nextcloud.members;
meta.teams = [ lib.teams.nextcloud ];
}
@@ -252,5 +252,5 @@ in
};
};
meta.maintainers = lib.teams.ngi.members;
meta.teams = [ lib.teams.ngi ];
}
@@ -0,0 +1,429 @@
{
pkgs,
config,
lib,
...
}:
let
cfg = config.services.speedtest-tracker;
user = cfg.user;
group = cfg.group;
defaultUser = "speedtest-tracker";
defaultGroup = "speedtest-tracker";
artisan = "${cfg.package}/artisan";
env-file-values = lib.mapAttrs' (n: v: {
name = lib.removeSuffix "_FILE" n;
value = v;
}) (lib.filterAttrs (n: v: v != null && lib.match ".+_FILE" n != null) cfg.settings);
env-nonfile-values = lib.filterAttrs (n: v: lib.match ".+_FILE" n == null) cfg.settings;
speedtest-tracker-maintenance = pkgs.writeShellScript "speedtest-tracker-maintenance.sh" ''
set -a
${lib.toShellVars env-nonfile-values}
${lib.concatLines (lib.mapAttrsToList (n: v: "${n}=\"$(< ${v})\"") env-file-values)}
set +a
${lib.optionalString (
cfg.settings.DB_CONNECTION == "sqlite"
) "touch ${cfg.dataDir}/storage/database/database.sqlite"}
rm -f ${cfg.dataDir}/cache/*.php
${artisan} package:discover
${artisan} migrate --force
${artisan} optimize:clear
${artisan} view:cache
${artisan} config:cache
'';
speedtest-tracker-env-script =
command:
pkgs.writeShellScript "speedtest-tracker-${builtins.replaceStrings [ ":" " " ] [ "-" "-" ] command}.sh" ''
set -a
${lib.toShellVars env-nonfile-values}
${lib.concatLines (lib.mapAttrsToList (n: v: "${n}=\"$(< ${v})\"") env-file-values)}
set +a
exec ${artisan} ${command}
'';
commonServiceConfig = {
Type = "oneshot";
User = user;
Group = group;
StateDirectory = "speedtest-tracker";
ReadWritePaths = [ cfg.dataDir ];
WorkingDirectory = cfg.package;
PrivateTmp = true;
PrivateDevices = true;
CapabilityBoundingSet = "";
AmbientCapabilities = "";
ProtectSystem = "strict";
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
ProtectClock = true;
ProtectHostname = true;
ProtectHome = "tmpfs";
ProtectKernelLogs = true;
ProtectProc = "invisible";
ProcSubset = "pid";
PrivateNetwork = false;
RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX";
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service @resources"
"~@obsolete @privileged"
];
RestrictSUIDSGID = true;
RemoveIPC = true;
NoNewPrivileges = true;
RestrictRealtime = true;
RestrictNamespaces = true;
LockPersonality = true;
PrivateUsers = true;
};
in
{
options.services.speedtest-tracker = {
enable = lib.mkEnableOption "Speedtest Tracker: A self-hosted internet performance tracking application";
package =
lib.mkPackageOption pkgs "speedtest-tracker" { }
// lib.mkOption {
apply =
speedtest-tracker:
speedtest-tracker.override (prev: {
dataDir = cfg.dataDir;
});
};
user = lib.mkOption {
type = lib.types.str;
default = defaultUser;
description = "User account under which Speedtest Tracker runs.";
};
group = lib.mkOption {
type = lib.types.str;
default = if cfg.enableNginx then "nginx" else defaultGroup;
defaultText = "If `services.speedtest-tracker.enableNginx` is true then `nginx` else ${defaultGroup}";
description = ''
Group under which Speedtest Tracker runs. It is best to set this to the group
of whatever webserver is being used as the frontend.
'';
};
dataDir = lib.mkOption {
type = lib.types.path;
default = "/var/lib/speedtest-tracker";
description = ''
The place where Speedtest Tracker stores its state.
'';
};
enableNginx = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether to enable nginx or not. If enabled, an nginx virtual host will
be created for access to Speedtest Tracker. If not enabled, then you may use
`''${config.services.speedtest-tracker.package}` as your document root in
whichever webserver you wish to setup.
'';
};
virtualHost = lib.mkOption {
type = lib.types.str;
default = "localhost";
description = ''
The hostname at which you wish Speedtest Tracker to be served. If you have
enabled nginx using `services.speedtest-tracker.enableNginx` then this will
be used.
'';
};
poolConfig = lib.mkOption {
type = lib.types.attrsOf (
lib.types.oneOf [
lib.types.str
lib.types.int
lib.types.bool
]
);
default = { };
defaultText = ''
{
"pm" = "dynamic";
"pm.max_children" = 32;
"pm.start_servers" = 2;
"pm.min_spare_servers" = 2;
"pm.max_spare_servers" = 4;
"pm.max_requests" = 500;
}
'';
description = ''
Options for the Speedtest Tracker PHP pool. See the documentation on `php-fpm.conf`
for details on configuration directives.
'';
};
settings = lib.mkOption {
default = { };
description = ''
Options for Speedtest Tracker configuration. Refer to
<https://github.com/alexjustesen/speedtest-tracker> for
details on supported values. All `_FILE` values are supported:
append `_FILE` to the setting name to provide a path to a file
containing the secret value.
'';
example = lib.literalExpression ''
{
APP_KEY_FILE = "/var/secrets/speedtest-tracker-app-key.txt";
DB_CONNECTION = "mysql";
DB_HOST = "db";
DB_PORT = 3306;
DB_DATABASE = "speedtest-tracker";
DB_USERNAME = "speedtest-tracker";
DB_PASSWORD_FILE = "/var/secrets/speedtest-tracker-mysql-password.txt";
}
'';
type = lib.types.submodule {
freeformType = lib.types.attrsOf (
lib.types.oneOf [
lib.types.str
lib.types.int
lib.types.bool
]
);
options = {
APP_KEY_FILE = lib.mkOption {
type = lib.types.path;
description = ''
The path to your appkey. The file should contain a 32 character
random app key. This may be set using `echo "base64:$(head -c 32
/dev/urandom | base64)" > /path/to/key-file`.
'';
};
APP_URL = lib.mkOption {
type = lib.types.str;
default =
if cfg.virtualHost == "localhost" then
"http://${cfg.virtualHost}"
else
"https://${cfg.virtualHost}";
defaultText = ''
http(s)://''${config.services.speedtest-tracker.virtualHost}
'';
description = ''
The APP_URL used by Speedtest Tracker internally. Please make sure this
URL matches the external URL of your installation. It is used to
validate specific requests and to generate URLs in notifications.
'';
};
DB_CONNECTION = lib.mkOption {
type = lib.types.enum [
"sqlite"
"mysql"
"mariadb"
"pgsql"
];
default = "sqlite";
example = "pgsql";
description = ''
The type of database you wish to use. Can be one of "sqlite",
"mysql", "mariadb" or "pgsql".
'';
};
DB_HOST = lib.mkOption {
type = lib.types.str;
default = "localhost";
description = ''
The IP or hostname which hosts your database.
'';
};
DB_PORT = lib.mkOption {
type = lib.types.nullOr lib.types.int;
default =
if cfg.settings.DB_CONNECTION == "pgsql" then
5432
else if cfg.settings.DB_CONNECTION == "mysql" || cfg.settings.DB_CONNECTION == "mariadb" then
3306
else
null;
defaultText = ''
`null` if DB_CONNECTION is "sqlite", `3306` if "mysql" or "mariadb", `5432` if "pgsql"
'';
description = ''
The port your database is listening at. sqlite does not require
this value to be filled.
'';
};
DB_DATABASE = lib.mkOption {
type = lib.types.str;
default =
if cfg.settings.DB_CONNECTION == "sqlite" then
"${cfg.dataDir}/storage/database/database.sqlite"
else
"speedtest-tracker";
defaultText = ''
"''${config.services.speedtest-tracker.dataDir}/storage/database/database.sqlite" if DB_CONNECTION is "sqlite", "speedtest-tracker" otherwise
'';
description = ''
The name of the database, or path to the sqlite file.
'';
};
};
};
};
};
config = lib.mkIf cfg.enable {
services.phpfpm.pools.speedtest-tracker = {
inherit user group;
phpPackage = cfg.package.phpPackage;
phpOptions = ''
log_errors = on
'';
settings = {
"listen.mode" = lib.mkDefault "0660";
"listen.owner" = lib.mkDefault user;
"listen.group" = lib.mkDefault group;
"pm" = lib.mkDefault "dynamic";
"pm.max_children" = lib.mkDefault 32;
"pm.start_servers" = lib.mkDefault 2;
"pm.min_spare_servers" = lib.mkDefault 2;
"pm.max_spare_servers" = lib.mkDefault 4;
"pm.max_requests" = lib.mkDefault 500;
}
// cfg.poolConfig;
};
systemd.services.speedtest-tracker-setup = {
after = [
"postgresql.target"
"mysql.service"
];
requiredBy = [ "phpfpm-speedtest-tracker.service" ];
before = [ "phpfpm-speedtest-tracker.service" ];
serviceConfig = {
ExecStart = speedtest-tracker-maintenance;
RemainAfterExit = true;
}
// commonServiceConfig;
unitConfig.JoinsNamespaceOf = "phpfpm-speedtest-tracker.service";
restartTriggers = [ cfg.package ];
partOf = [ "phpfpm-speedtest-tracker.service" ];
};
systemd.services.speedtest-tracker-scheduler = {
after = [ "speedtest-tracker-setup.service" ];
wants = [ "speedtest-tracker-setup.service" ];
description = "Speedtest Tracker scheduler";
path = [ pkgs.ookla-speedtest ];
serviceConfig = {
ExecStart = speedtest-tracker-env-script "schedule:run";
}
// commonServiceConfig;
};
systemd.timers.speedtest-tracker-scheduler = {
description = "Speedtest Tracker scheduler timer";
timerConfig = {
OnCalendar = "minutely";
Persistent = true;
};
wantedBy = [ "timers.target" ];
restartTriggers = [ cfg.package ];
};
systemd.services.speedtest-tracker-queue-worker = {
after = [ "speedtest-tracker-setup.service" ];
wants = [ "speedtest-tracker-setup.service" ];
wantedBy = [ "multi-user.target" ];
description = "Speedtest Tracker queue worker";
path = [ pkgs.ookla-speedtest ];
serviceConfig = commonServiceConfig // {
Type = "simple";
Restart = "always";
ExecStart = speedtest-tracker-env-script "queue:work --sleep=3 --tries=3";
};
};
services.nginx = lib.mkIf cfg.enableNginx {
enable = true;
recommendedTlsSettings = lib.mkDefault true;
recommendedOptimisation = lib.mkDefault true;
recommendedGzipSettings = lib.mkDefault true;
virtualHosts.${cfg.virtualHost} = {
root = "${cfg.package}/public";
locations = {
"/" = {
tryFiles = "$uri $uri/ /index.php?$query_string";
index = "index.php";
extraConfig = ''
sendfile off;
'';
};
"~ \\.php$" = {
extraConfig = ''
include ${config.services.nginx.package}/conf/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param modHeadersAvailable true;
fastcgi_pass unix:${config.services.phpfpm.pools.speedtest-tracker.socket};
'';
};
"~ \\.(js|css|gif|png|ico|jpg|jpeg)$" = {
extraConfig = "expires 365d;";
};
};
};
};
systemd.tmpfiles.settings."10-speedtest-tracker" =
lib.genAttrs
[
"${cfg.dataDir}/storage"
"${cfg.dataDir}/storage/app"
"${cfg.dataDir}/storage/database"
"${cfg.dataDir}/storage/framework"
"${cfg.dataDir}/storage/framework/cache"
"${cfg.dataDir}/storage/framework/sessions"
"${cfg.dataDir}/storage/framework/views"
"${cfg.dataDir}/storage/logs"
"${cfg.dataDir}/cache"
]
(n: {
d = {
inherit group;
mode = "0700";
inherit user;
};
})
// {
"${cfg.dataDir}".d = {
inherit group;
mode = "0710";
inherit user;
};
};
users = {
users = lib.mkIf (user == defaultUser) {
${defaultUser} = {
inherit group;
isSystemUser = true;
home = cfg.dataDir;
};
};
groups = lib.mkIf (group == defaultGroup) { ${defaultGroup} = { }; };
};
};
meta.maintainers = pkgs.speedtest-tracker.meta.maintainers;
}
@@ -25,7 +25,7 @@ in
{
meta = {
maintainers = teams.enlightenment.members;
teams = [ teams.enlightenment ];
};
imports = [
@@ -16,7 +16,7 @@ in
{
meta = {
maintainers = teams.lumina.members;
teams = [ teams.lumina ];
};
options = {
@@ -15,7 +15,7 @@ in
{
meta = {
maintainers = teams.lxqt.members;
teams = [ teams.lxqt ];
};
options = {
@@ -15,7 +15,7 @@ let
in
{
meta = {
maintainers = teams.xfce.members;
teams = [ teams.xfce ];
};
imports = [
@@ -40,6 +40,6 @@ python3.pkgs.buildPythonApplication {
'';
meta = {
maintainers = [ ] ++ lib.teams.pantheon.members;
teams = [ lib.teams.pantheon ];
};
}
@@ -13,7 +13,7 @@ let
in
{
meta.maintainers = lib.teams.lomiri.members;
meta.teams = [ lib.teams.lomiri ];
options = {
services.xserver.displayManager.lightdm.greeters.lomiri = {
@@ -16,7 +16,7 @@ let
in
{
meta = {
maintainers = [ ] ++ lib.teams.pantheon.members;
teams = [ lib.teams.pantheon ];
};
options = {
@@ -73,7 +73,7 @@ let
in
{
meta = {
maintainers = [ ] ++ lib.teams.pantheon.members;
teams = [ lib.teams.pantheon ];
};
# Note: the order in which lightdm greeter modules are imported
+1 -1
View File
@@ -13,7 +13,7 @@ let
in
{
meta = {
maintainers = teams.pantheon.members;
teams = [ teams.pantheon ];
};
###### interface
+1 -1
View File
@@ -13,7 +13,7 @@ let
in
{
meta = {
maintainers = [ ] ++ lib.teams.podman.members;
teams = [ lib.teams.podman ];
};
options.virtualisation.containers = {
+1 -1
View File
@@ -21,7 +21,7 @@ let
in
{
meta = {
maintainers = teams.podman.members;
teams = [ teams.podman ];
};
options.virtualisation.cri-o = {
+1 -1
View File
@@ -10,7 +10,7 @@ let
in
{
meta = {
maintainers = lib.teams.lxc.members;
teams = [ lib.teams.lxc ];
};
options = {
@@ -10,7 +10,7 @@ let
in
{
meta = {
maintainers = lib.teams.lxc.members;
teams = [ lib.teams.lxc ];
};
imports = [
+1 -1
View File
@@ -176,7 +176,7 @@ let
in
{
meta = {
maintainers = lib.teams.lxc.members;
teams = [ lib.teams.lxc ];
};
options = {
@@ -7,7 +7,7 @@
{
meta = {
maintainers = lib.teams.lxc.members;
teams = [ lib.teams.lxc ];
};
imports = [
@@ -71,7 +71,7 @@ in
];
meta = {
maintainers = lib.teams.lxc.members;
teams = [ lib.teams.lxc ];
};
options = {

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