Merge staging-next into staging

This commit is contained in:
nixpkgs-ci[bot]
2026-09-28 12:16:14 +00:00
committed by GitHub
133 changed files with 2721 additions and 2143 deletions

View File

@@ -290,6 +290,10 @@
To prevent loading the `early-default` library,
set `inhibit-early-default-init` in `early-init.el`.
- Ceph has a vulnerability in old generated CephX keys.
The project recommends to rotate old keys.
This is a manual process, see https://ceph.io/en/news/blog/2026/v20-2-4-v19-2-6-combo-released
- `services.ceph` enabled the generation of Ceph log files at `/var/log/ceph/`.
They were missing before because Ceph omitted logs when this directory was missing.
Ceph logs can grow large, so you may want to configure rotation of these logs.

View File

@@ -251,7 +251,7 @@
};
_365tuwe = {
name = "Uwe Schlifkowitz";
email = "supertuwe@gmail.com";
email = "uwe.schlifkowitz@secunet.com";
github = "365tuwe";
githubId = 10263091;
};
@@ -10812,6 +10812,12 @@
githubId = 273582;
name = "greg";
};
gregl83 = {
email = "general+nixpkgs@gregorylanglais.com";
github = "gregl83";
githubId = 1258023;
name = "gregory langlais";
};
gregshuflin = {
email = "greg@everdayimshuflin.com";
github = "neunenak";
@@ -12657,6 +12663,12 @@
github = "j0hax";
githubId = 3802620;
};
j0schu = {
name = "Jonas";
email = "Joschu2015@t-online.de";
github = "J0schu";
githubId = 56407950;
};
j0xaf = {
email = "j0xaf@j0xaf.de";
name = "Jörn Gersdorf";
@@ -31610,10 +31622,10 @@
];
};
wrench-exile-legacy = {
email = "user@wrench-exile-legacy.site";
email = "hello@wrenchd.dev";
github = "wrench-exile-legacy";
githubId = 280737824;
name = "wrench";
name = "wrenchd";
};
wrmilling = {
name = "Winston R. Milling";

View File

@@ -302,9 +302,11 @@
- `chatgpt` has been retargeted to OpenAI's new ChatGPT desktop app, while the previous app has been renamed to `chatgpt-classic`.
- NetBox was updated to `>= 4.6.8`. Have a look at the breaking changes
of the [4.6 release](https://github.com/netbox-community/netbox/releases/tag/v4.6.0),
make the required changes to your database, if needed, then upgrade by setting `services.netbox.package = pkgs.netbox_4_6;` in your configuration.
- NetBox was updated to `>= 4.7.0`. Have a look at the breaking changes
of the [4.6 release](https://github.com/netbox-community/netbox/releases/tag/v4.6.0)
and the [4.7 release](https://github.com/netbox-community/netbox/releases/tag/v4.7.0),
make the required changes to your configuration and database, if needed,
before you upgrade to NixOS 26.11.
- The COSMIC desktop module now enables by default `system76-power` and `system76-scheduler` following upstream recommended packages. The previous power managment service can be enabled back by setting `services.power-profiles-daemon.enable = true`.
@@ -356,6 +358,8 @@
- `boot.loader.systemd-boot` gained support for [Automatic Boot Assessment](https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT/) via the new [`boot.loader.systemd-boot.bootCounting`](#opt-boot.loader.systemd-boot.bootCounting.enable) options, allowing automatic detection of and recovery from bad NixOS generations. As part of this change, boot loader entries on the ESP/XBOOTLDR partition are now named `nixos-<content-hash>.conf` instead of `nixos-generation-<n>.conf`; existing entries are migrated automatically on the next `nixos-rebuild boot`/`switch`.
- The Wordpress module now supports auto database migrations using wp-cli, which gets triggered after every package version update.
- `services.nginx` gained a [`lua`](#opt-services.nginx.lua.enable) option to enable Lua scripting via OpenResty's lua-nginx-module on a stock nginx, configuring `lua_package_path`/`lua_package_cpath` from the packages listed in [`services.nginx.lua.extraPackages`](#opt-services.nginx.lua.extraPackages). Use this to add Lua to a regular nginx; for the full OpenResty platform (libraries that rely on its bundled lualib, such as `lua-resty-openidc`), set `services.nginx.package` to `pkgs.openresty` instead — the option configures the Lua search path for it too.
- `services.nginx.virtualHosts.<name>.locations.<name>` gained a new `useGrpcErrorPages` option. If enabled, it sets up error pages that are valid gRPC messages. This is useful if you proxy gRPC and want to emit errors from nginx, for example when adding authentication on top.

View File

@@ -72,6 +72,20 @@ $ nixos-version --configuration-revision
aa314ebd1592f6cdd53cb5bba8bcae97d9323de8
.Ed
.
.It Fl -kernel-version
Show the kernel version, e.g.
.Bd -literal -offset indent
$ nixos-version --kernel-version
7.2.5
.Ed
.
.It Fl -specialisations
Show specialisations, separated by spaces, if available, e.g.
.Bd -literal -offset indent
$ nixos-version --specialisations
foo bar
.Ed
.
.It Fl -json
Print a JSON representation of the versions of NixOS and the top-level
configuration flake.

View File

@@ -20,8 +20,23 @@ case "$1" in
fi
echo "@configurationRevision@"
;;
--kernel-version)
if [[ "@kernelVersion@" =~ "@" ]]; then
echo "$0: kernel version is unknown" >&2
exit 1
fi
echo "@kernelVersion@"
;;
--specialisations)
specialisations=@specialisations@
if [[ -z "$specialisations" ]]; then
echo "$0: no specialisations found" >&2
exit 1
fi
printf '%s\n' "$specialisations"
;;
--json)
cat <<EOF
cat <<'EOF'
@json@
EOF
;;

View File

@@ -53,13 +53,27 @@ let
nixos-version = makeProg {
name = "nixos-version";
src = ./nixos-version.sh;
replacements = {
replacements = rec {
inherit (pkgs) runtimeShell;
inherit (config.system.nixos) version codeName revision;
inherit (config.system) configurationRevision;
kernelVersion =
if config.boot.kernel.enable then
# modDirVersion returns 6.18.54-xanmod1 instead of 6.18.54
config.boot.kernelPackages.kernel.modDirVersion or config.boot.kernelPackages.kernel.version
else
null;
specialisations = lib.escapeShellArg (
lib.concatStringsSep " " (lib.attrNames config.specialisation)
);
json = builtins.toJSON (
{
nixosVersion = config.system.nixos.version;
specialisations = lib.attrNames config.specialisation;
}
// lib.optionalAttrs (kernelVersion != null) {
inherit kernelVersion;
}
// lib.optionalAttrs (config.system.nixos.revision != null) {
nixpkgsRevision = config.system.nixos.revision;
@@ -292,7 +306,7 @@ in
{
options.system.tools.${name}.enable = lib.mkEnableOption "${name} script" // {
default = config.nix.enable && !config.system.disableInstallerTools;
defaultText = "config.nix.enable && !config.system.disableInstallerTools";
defaultText = lib.literalExpression "config.nix.enable && !config.system.disableInstallerTools";
};
config = lib.mkIf config.system.tools.${name}.enable {

View File

@@ -1258,7 +1258,6 @@
./services/networking/gnunet.nix
./services/networking/go-autoconfig.nix
./services/networking/go-camo.nix
./services/networking/go-neb.nix
./services/networking/go-shadowsocks2.nix
./services/networking/gobgpd.nix
./services/networking/godns.nix

View File

@@ -486,6 +486,10 @@ in
See https://www.isc.org/blogs/isc-dhcp-eol/ for details.
Please switch to a different implementation like kea or dnsmasq.
'')
(lib.mkRemovedOptionModule [ "services" "go-neb" ] ''
The Go-NEB project was discontinued by Matrix.org and archived in June
2023. Use matrix-hookshot or another maintained Matrix bot instead.
'')
(mkRemovedOptionModule [ "services" "gsignond" ] ''
The corresponding package was unmaintained, abandoned upstream, used outdated library and thus removed from nixpkgs.
'')

View File

@@ -20,11 +20,16 @@ let
rawHomeserverUrl = cfg.homeserverUrl;
pantalaimon = {
inherit (cfg.pantalaimon) username;
use = cfg.pantalaimon.enable;
}
// lib.optionalAttrs cfg.pantalaimon.enable {
inherit (cfg.pantalaimon) username;
password = "@PANTALAIMON_PASSWORD@"; # will be replaced in "generateConfig"
};
encryption = {
inherit (cfg.settings.encryption) username;
password = "@ENCRYPTION_PASSWORD@"; # will be replaced in "generateConfig"
};
};
moduleConfigFile = pkgs.writeText "module-config.yaml" (
@@ -72,6 +77,9 @@ let
${lib.optionalString (cfg.pantalaimon.passwordFile != null) ''
${pkgs.replace-secret}/bin/replace-secret '@PANTALAIMON_PASSWORD@' '${cfg.pantalaimon.passwordFile}' ${cfg.dataPath}/config/default.yaml
''}
${lib.optionalString (cfg.encryption.passwordFile != null) ''
${pkgs.replace-secret}/bin/replace-secret '@ENCRYPTION_PASSWORD@' '${cfg.encryption.passwordFile}' ${cfg.dataPath}/config/default.yaml
''}
''
);
in
@@ -98,6 +106,14 @@ in
'';
};
encryption.passwordFile = lib.mkOption {
type = with lib.types; nullOr path;
default = null;
description = ''
File containing the matrix password for the `mjolnir` user.
'';
};
pantalaimon = lib.mkOption {
description = ''
`pantalaimon` options (enables E2E Encryption support).
@@ -186,17 +202,22 @@ in
config = lib.mkIf config.services.mjolnir.enable {
assertions = [
{
assertion = !(cfg.settings.encryption.use && cfg.encryption.passwordFile == null);
message = "encryption.passwordFile must be specified when native encryption is used.";
}
{
assertion = !(cfg.pantalaimon.enable && cfg.pantalaimon.passwordFile == null);
message = "Specify pantalaimon.passwordFile";
message = "pantalaimon.passwordFile must be specified when pantalaimon is enabled.";
}
{
assertion = !(cfg.pantalaimon.enable && cfg.accessTokenFile != null);
message = "Do not specify accessTokenFile when using pantalaimon";
assertion = cfg.accessTokenFile == null -> cfg.pantalaimon.enable || cfg.settings.encryption.use;
message = "Do not specify accessTokenFile when using native encryption or pantalaimon";
}
{
assertion = !(!cfg.pantalaimon.enable && cfg.accessTokenFile == null);
message = "Specify accessTokenFile when not using pantalaimon";
assertion =
!(!cfg.pantalaimon.enable && !cfg.settings.encryption.use && cfg.accessTokenFile == null);
message = "Specify accessTokenFile when not using pantalaimon or native encryption.";
}
];

View File

@@ -713,7 +713,9 @@ in
"d '${cfg.exporter.directory}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
];
services.paperless.exporter.settings = options.services.paperless.exporter.settings.default;
services.paperless.exporter.settings = lib.mapAttrs (
_: v: lib.mkDefault v
) options.services.paperless.exporter.settings.default;
systemd.services.paperless-exporter = {
startAt = lib.defaultTo [ ] cfg.exporter.onCalendar;

View File

@@ -240,6 +240,8 @@ in
"AF_INET"
"AF_INET6"
]
# AF_UNIX to be able to connect to e.g. /dev/log
++ lib.optionals (cfg.settings.log.file or "" == "syslog") [ "AF_UNIX" ]
++ lib.optionals cfg.allowDHCP [ "AF_PACKET" ];
RestrictNamespaces = true;
RestrictRealtime = true;

View File

@@ -1,10 +0,0 @@
{ lib, ... }:
{
imports = [
(lib.mkRemovedOptionModule [ "services" "go-neb" ] ''
The Go-NEB project was discontinued by Matrix.org and archived in June
2023. Use matrix-hookshot or another maintained Matrix bot instead.
'')
];
}

View File

@@ -116,7 +116,7 @@ in
services.phpfpm.pools.engelsystem = {
user = "engelsystem";
settings = {
settings = lib.mapAttrs (_: v: lib.mkDefault v) {
"listen.owner" = config.services.nginx.user;
"pm" = "dynamic";
"pm.max_children" = 32;

View File

@@ -434,10 +434,10 @@ in
package = lib.mkOption {
type = types.package;
default =
if lib.versionAtLeast config.system.stateVersion "26.11" then pkgs.netbox_4_6 else pkgs.netbox_4_5;
if lib.versionAtLeast config.system.stateVersion "26.11" then pkgs.netbox_4_7 else pkgs.netbox_4_5;
defaultText = lib.literalExpression ''
if lib.versionAtLeast config.system.stateVersion "26.11" then
pkgs.netbox_4_6
pkgs.netbox_4_7
else
pkgs.netbox_4_5;
'';
@@ -563,6 +563,15 @@ in
config = lib.mkIf cfg.enable (
lib.mkMerge [
{
assertions = [
{
assertion =
cfg.postgresql.createLocally
-> lib.versionAtLeast config.services.postgresql.finalPackage.version "15";
message = "NetBox requires PostgreSQL >= 15. Please read the NixOS manual to upgrade your PostgreSQL version.";
}
];
services.netbox.plugins = lib.mkIf enableLDAP (ps: [ ps.django-auth-ldap ]);
services.redis.servers.netbox.enable = cfg.redis.createLocally;
@@ -733,26 +742,6 @@ in
PrivateTmp = true;
};
};
netbox-housekeeping = defaultUnitConfig // {
description = "NetBox housekeeping job";
wantedBy = [ "multi-user.target" ];
after = [
"network-online.target"
"netbox.service"
];
wants = [ "network-online.target" ];
serviceConfig = defaultServiceConfig // {
Type = "oneshot";
ExecStart = toString [
(lib.getExe finalPackage)
"housekeeping"
];
};
};
};
systemd.timers.netbox-housekeeping = {

View File

@@ -555,7 +555,33 @@ in
before = [ "phpfpm-wordpress-${hostName}.service" ];
after = optional cfg.database.createLocally "mysql.service";
script = secretsScript (stateDir hostName);
serviceConfig = {
Type = "oneshot";
User = user;
Group = webserver.group;
};
})
) eachSite)
(mapAttrs' (
hostName: cfg:
(nameValuePair "wordpress-migrate-database-${hostName}" {
wantedBy = [ "multi-user.target" ];
after = [
"phpfpm-wordpress-${hostName}.service"
]
++ optional cfg.database.createLocally "mysql.service";
script = ''
# Auto migrate database after version update
versionFile="${stateDir hostName}/src-version"
version=$(cat "$versionFile" 2>/dev/null || echo 0)
if [[ $version != 0 && $version != ${cfg.package.version} ]]; then
echo "Executing database migration"
${lib.getExe pkgs.wp-cli} --path="${cfg.finalPackage}/share/wordpress" \
--skip-plugins --skip-themes core update-db
fi
echo ${cfg.package.version} > "$versionFile"
'';
serviceConfig = {
Type = "oneshot";
User = user;

View File

@@ -9,6 +9,10 @@ let
cfg = config.boot.kexec;
in
{
meta = {
inherit (pkgs.kexec-tools.meta) maintainers;
};
options.boot.kexec = {
enable = lib.mkEnableOption "kexec" // {
default = lib.meta.availableOn pkgs.stdenv.hostPlatform pkgs.kexec-tools;

View File

@@ -22,6 +22,14 @@
};
};
syslogConf = {
services.adguardhome = {
enable = true;
settings.log.file = "syslog";
};
};
declarativeConf = {
services.adguardhome = {
enable = true;
@@ -127,6 +135,12 @@
schemaVersionBefore23.wait_for_unit("adguardhome.service")
schemaVersionBefore23.wait_for_open_port(3000)
with subtest("Logging to syslog test"):
# AdGuard is expected to fail when it cannot connect to syslog
# hence its sufficient to look whether the service starts at all
syslogConf.wait_for_unit("adguardhome.service")
syslogConf.wait_for_open_port(3000)
with subtest("Declarative config test, DNS will be reachable"):
declarativeConf.wait_for_unit("adguardhome.service")
declarativeConf.wait_for_open_port(53)

View File

@@ -402,22 +402,10 @@ in
ceph-multi-node-bluestore-cephfs = runTestOn [ "aarch64-linux" "x86_64-linux" ] (
import ./ceph-multi-node-bluestore.nix { withCephfs = true; }
);
ceph-multi-node-deprecated-filestore = runTestOn [
"aarch64-linux"
"x86_64-linux"
] ./ceph-multi-node-deprecated-filestore.nix;
ceph-single-node-bluestore = runTestOn [
"aarch64-linux"
"x86_64-linux"
] ./ceph-single-node-bluestore.nix;
ceph-single-node-bluestore-dmcrypt = runTestOn [
"aarch64-linux"
"x86_64-linux"
] ./ceph-single-node-bluestore-dmcrypt.nix;
ceph-single-node-deprecated-filestore = runTestOn [
"aarch64-linux"
"x86_64-linux"
] ./ceph-single-node-deprecated-filestore.nix;
certmgr = import ./certmgr.nix { inherit pkgs runTest; };
cfssl = runTestOn [ "aarch64-linux" "x86_64-linux" ] ./cfssl.nix;
cgit = runTest ./cgit.nix;

View File

@@ -25,19 +25,16 @@ let
osd0 = {
name = "0";
ip = "192.168.1.2";
key = "AQBCEJNa3s8nHRAANvdsr93KqzBznuIWm2gOGg==";
uuid = "55ba2294-3e24-478f-bee0-9dca4c231dd9";
};
osd1 = {
name = "1";
ip = "192.168.1.3";
key = "AQBEEJNac00kExAAXEgy943BGyOpVH1LLlHafQ==";
uuid = "5e97a838-85b6-43b0-8950-cb56d554d1e5";
};
osd2 = {
name = "2";
ip = "192.168.1.4";
key = "AQAdyhZeIaUlARAAGRoidDAmS6Vkp546UFEf5w==";
uuid = "ea999274-13d0-4dd5-9af9-ad25a324f72f";
};
# Client that mounts CephFS using the in-kernel client.
@@ -58,6 +55,14 @@ let
monHost = cfg.monA.ip;
monInitialMembers = cfg.monA.name;
};
extraConfig = {
log_to_syslog = "false";
log_to_file = "false";
log_to_stderr = "true";
debug_rocksdb = "1/5";
debug_mgr = "1/5";
mon_host = "v2:${cfg.monA.ip}:3300 v1:${cfg.monA.ip}:6789";
};
}
// daemonConfig;
@@ -81,6 +86,7 @@ let
bash
sudo
ceph
cryptsetup
netcat
];
@@ -145,6 +151,11 @@ let
enable = true;
daemons = [ cfg.monA.name ];
};
# TODO: move this to a separate machine
rgw = {
enable = true;
daemons = [ cfg.monA.name ];
};
}
# The MDS daemon (which provides CephFS) is only configured in the CephFS
# variant of this test.
@@ -209,6 +220,11 @@ let
vlans = [ 1 ];
};
# Ceph 20.2.4 introduced the aes256k cipher for authentication.
# Linux started supporting these in kernel version 7.0.
# Remove this line at the earliest convenience (i.e. when tests are run by 7.0 or higher by default).
boot.kernelPackages = pkgs.linuxPackages_latest;
networking = networkConfig;
environment.systemPackages = with pkgs; [
@@ -285,6 +301,8 @@ let
# Based on the "manual deployment" approach from:
# https://docs.ceph.com/en/tentacle/install/manual-deployment/
baseScript = ''
import json
start_all()
monA.wait_for_unit("network.target")
@@ -297,14 +315,15 @@ let
"sudo -u ceph ceph-authtool --create-keyring /tmp/ceph.mon.keyring --gen-key -n mon. --cap mon 'allow *'",
"sudo -u ceph ceph-authtool --create-keyring /etc/ceph/ceph.client.admin.keyring --gen-key -n client.admin --cap mon 'allow *' --cap osd 'allow *' --cap mds 'allow *' --cap mgr 'allow *'",
"sudo -u ceph ceph-authtool /tmp/ceph.mon.keyring --import-keyring /etc/ceph/ceph.client.admin.keyring",
"monmaptool --create --add ${cfg.monA.name} ${cfg.monA.ip} --fsid ${cfg.clusterId} /tmp/monmap",
# Creating the mon with v2 (and a legacy v1) address right away removes the need for running `enable-msgr2` later on.
# It is also makes the test more consistent by fixing the address to a known value instead of letting it derive the address.
"monmaptool --create --addv ${cfg.monA.name} '[v2:${cfg.monA.ip}:3300,v1:${cfg.monA.ip}:6789]' --auth-allowed-ciphers aes256k --auth-preferred-cipher aes256k --auth-service-cipher aes256k --fsid ${cfg.clusterId} /tmp/monmap",
"sudo -u ceph ceph-mon --mkfs -i ${cfg.monA.name} --monmap /tmp/monmap --keyring /tmp/ceph.mon.keyring",
"sudo -u ceph mkdir -p /var/lib/ceph/mgr/ceph-${cfg.monA.name}/",
"sudo -u ceph touch /var/lib/ceph/mon/ceph-${cfg.monA.name}/done",
"systemctl start ceph-mon-${cfg.monA.name}",
)
monA.wait_for_unit("ceph-mon-${cfg.monA.name}")
monA.succeed("ceph mon enable-msgr2")
monA.succeed("ceph config set mon auth_allow_insecure_global_id_reclaim false")
# Can't check ceph status until a mon is up
@@ -320,59 +339,63 @@ let
monA.wait_until_succeeds("ceph -s | grep 'quorum ${cfg.monA.name}'")
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
# Send the admin keyring to the OSD machines.
monA.succeed("cp /etc/ceph/ceph.client.admin.keyring /tmp/shared")
osd0.succeed("cp /tmp/shared/ceph.client.admin.keyring /etc/ceph")
osd1.succeed("cp /tmp/shared/ceph.client.admin.keyring /etc/ceph")
osd2.succeed("cp /tmp/shared/ceph.client.admin.keyring /etc/ceph")
# Send the bootstrap-osd keyring to the OSD machines.
monA.succeed("ceph auth get client.bootstrap-osd -o /etc/ceph/ceph.client.bootstrap-osd.keyring")
monA.succeed("cp /etc/ceph/ceph.client.bootstrap-osd.keyring /tmp/shared")
# Bootstrap the BlueStore OSDs.
osd0.succeed(
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd0.name}",
"echo bluestore > /var/lib/ceph/osd/ceph-${cfg.osd0.name}/type",
"ln -sf /dev/vdb /var/lib/ceph/osd/ceph-${cfg.osd0.name}/block",
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd0.name}/keyring --name osd.${cfg.osd0.name} --add-key ${cfg.osd0.key}",
'echo \'{"cephx_secret": "${cfg.osd0.key}"}\' | ceph osd new ${cfg.osd0.uuid} -i -',
)
osd1.succeed(
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd1.name}",
"echo bluestore > /var/lib/ceph/osd/ceph-${cfg.osd1.name}/type",
"ln -sf /dev/vdb /var/lib/ceph/osd/ceph-${cfg.osd1.name}/block",
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd1.name}/keyring --name osd.${cfg.osd1.name} --add-key ${cfg.osd1.key}",
'echo \'{"cephx_secret": "${cfg.osd1.key}"}\' | ceph osd new ${cfg.osd1.uuid} -i -',
)
osd2.succeed(
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd2.name}",
"echo bluestore > /var/lib/ceph/osd/ceph-${cfg.osd2.name}/type",
"ln -sf /dev/vdb /var/lib/ceph/osd/ceph-${cfg.osd2.name}/block",
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd2.name}/keyring --name osd.${cfg.osd2.name} --add-key ${cfg.osd2.key}",
'echo \'{"cephx_secret": "${cfg.osd2.key}"}\' | ceph osd new ${cfg.osd2.uuid} -i -',
)
#
# The steps for this are roughly the same for all OSDs:
# 1. get the bootstrap-osd keyring
# 2. prepare the osd via ceph-volume lvm, the second line contains the OSD specific configuration
# 3. deactivate it to unmount the tmpfs
# 4. activate it without a tmpfs for persistent data
# 5. sync, so the osd has at least one consistent state saved
# 6. start it
# We `sync` so that the config survives the forced crashes below.
# osd.0: plain
osd0.succeed(
"ceph-osd -i ${cfg.osd0.name} --mkfs --osd-uuid ${cfg.osd0.uuid}",
"chown -R ceph:ceph /var/lib/ceph/osd",
"mkdir -p /var/lib/ceph/bootstrap-osd",
"cp /tmp/shared/ceph.client.bootstrap-osd.keyring /var/lib/ceph/bootstrap-osd/ceph.keyring",
"ceph-volume lvm prepare --objectstore bluestore --no-systemd --osd-id ${cfg.osd0.name} --osd-fsid ${cfg.osd0.uuid} "
"--data /dev/vdb",
"ceph-volume lvm deactivate ${cfg.osd0.name} ${cfg.osd0.uuid}",
"ceph-volume lvm activate --no-tmpfs --no-systemd ${cfg.osd0.name} ${cfg.osd0.uuid}",
"sync",
"systemctl start ceph-osd-${cfg.osd0.name}",
)
# osd.1: plain
osd1.succeed(
"ceph-osd -i ${cfg.osd1.name} --mkfs --osd-uuid ${cfg.osd1.uuid}",
"chown -R ceph:ceph /var/lib/ceph/osd",
"mkdir -p /var/lib/ceph/bootstrap-osd",
"cp /tmp/shared/ceph.client.bootstrap-osd.keyring /var/lib/ceph/bootstrap-osd/ceph.keyring",
"ceph-volume lvm prepare --objectstore bluestore --no-systemd --osd-id ${cfg.osd1.name} --osd-fsid ${cfg.osd1.uuid} "
"--data /dev/vdb --dmcrypt",
"ceph-volume lvm deactivate ${cfg.osd1.name} ${cfg.osd1.uuid}",
"ceph-volume lvm activate --no-tmpfs --no-systemd ${cfg.osd1.name} ${cfg.osd1.uuid}",
"sync",
"systemctl start ceph-osd-${cfg.osd1.name}",
)
# osd.2: plain
osd2.succeed(
"ceph-osd -i ${cfg.osd2.name} --mkfs --osd-uuid ${cfg.osd2.uuid}",
"chown -R ceph:ceph /var/lib/ceph/osd",
"mkdir -p /var/lib/ceph/bootstrap-osd",
"cp /tmp/shared/ceph.client.bootstrap-osd.keyring /var/lib/ceph/bootstrap-osd/ceph.keyring",
"ceph-volume lvm prepare --objectstore bluestore --no-systemd --osd-fsid ${cfg.osd2.uuid} --osd-id ${cfg.osd2.name} "
"--data /dev/vdb",
"ceph-volume lvm deactivate ${cfg.osd2.name} ${cfg.osd2.uuid}",
"ceph-volume lvm activate --no-tmpfs --no-systemd ${cfg.osd2.name} ${cfg.osd2.uuid}",
"sync",
"systemctl start ceph-osd-${cfg.osd2.name}",
)
monA.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
monA.succeed(
# Autoscaling will cause PGs to be peering, causing the tests to become flakey.
"ceph osd pool set noautoscale",
"ceph osd pool create multi-node-test 32 32",
"ceph osd pool ls | grep 'multi-node-test'",
@@ -389,6 +412,7 @@ let
"ceph osd pool ls | grep 'multi-node-other-test'",
)
monA.succeed("ceph osd pool set multi-node-other-test size 2")
# TODO: actually write to the pool using rados directly
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
monA.wait_until_succeeds("! ceph -s | grep -e 'unknown' -e 'pgs inactive'")
monA.fail(
@@ -396,23 +420,100 @@ let
"ceph osd pool delete multi-node-other-test multi-node-other-test --yes-i-really-really-mean-it",
)
# Bootstrap RGW
monA.succeed(
"sudo -u ceph mkdir -p /var/lib/ceph/radosgw/ceph-${cfg.monA.name}",
"ceph auth get-or-create client.${cfg.monA.name} osd 'allow rwx' mon 'allow rw' > /var/lib/ceph/radosgw/ceph-${cfg.monA.name}/keyring",
"chown ceph:ceph /var/lib/ceph/radosgw/ceph-${cfg.monA.name}/keyring",
"systemctl start ceph-rgw-${cfg.monA.name}",
)
monA.wait_for_unit("ceph-rgw-${cfg.monA.name}")
monA.wait_for_open_port(7480)
# Enable the dashboard and recheck health
monA.succeed(
"ceph mgr module enable dashboard",
"ceph config set mgr mgr/dashboard/ssl false",
# default is 8080 but it's better to be explicit
"ceph config set mgr mgr/dashboard/server_port 8080",
)
# The dashboard does not listen on localhost:
# `server_addr` defaults to the wildcard address, but the dashboard module
# resolves that to the active mgr's own IP and binds only to it,
# so loopback is never bound.
# See https://github.com/ceph/ceph/blob/v20.2.2/src/pybind/mgr/dashboard/module.py#L213-L214
# Therefore address the dashboard via the mgr's IP instead of localhost.
dashboard = "http://${cfg.monA.ip}:8080"
monA.wait_for_open_port(8080, addr="${cfg.monA.ip}")
monA.wait_until_succeeds(f"curl -s --fail {dashboard}")
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
# Initialize dashboard creds.
# In a the query below, we test the Dashboard's `/api/rgw/daemon`,
# which needs that the dashboard can talk to RGW.
# `set-rgw-credentials` needs a running RGW daemon.
monA.succeed(
"echo 'foo bar baz qux' > /tmp/dashboard_pw",
"ceph dashboard ac-user-create admin -i /tmp/dashboard_pw administrator",
"ceph dashboard set-rgw-credentials",
"sync",
)
# Get dashboard auth token
auth_payload = json.dumps({"username": "admin", "password": "foo bar baz qux"})
auth_response = json.loads(monA.succeed(
f"curl --fail -s -X POST -H 'Accept: application/vnd.ceph.api.v1.0+json' -H 'Content-Type: application/json' -d '{auth_payload}' {dashboard}/api/auth",
))
token = auth_response["token"]
# Check cluster health via dashboard API
health = json.loads(monA.succeed(
f"curl --fail -s -H 'Accept: application/vnd.ceph.api.v1.0+json' -H 'Authorization: Bearer {token}' {dashboard}/api/health/minimal",
))
assert health["health"]["status"] == "HEALTH_OK"
# List daemons via REST API.
# This also requires a running RGW daemon, as it asserts on the first one.
rgw_daemons = json.loads(monA.succeed(
f"curl --fail -s -H 'Accept: application/vnd.ceph.api.v1.0+json' -H 'Authorization: Bearer {token}' {dashboard}/api/rgw/daemon",
))
assert rgw_daemons[0]["id"] == "${cfg.monA.name}"
# Shut down ceph on all machines in a very unpolite way
monA.crash()
osd0.crash()
osd1.crash()
osd2.crash()
# Start it up
# Start the mon first and mark the OSDs as down.
# Since the heartbeats are pretty high by default, the OSDs would otherwise be marked as up still.
# However we do not want to lower the heartbeats since this might cause flakey tests.
monA.start()
monA.wait_for_unit("ceph-mon-${cfg.monA.name}")
monA.wait_until_succeeds("ceph osd down all")
# Then start the OSDs as normal.
osd0.start()
osd1.start()
osd2.start()
monA.start()
# Ensure they are all up.
osd0.wait_for_unit("network.target")
osd1.wait_for_unit("network.target")
osd2.wait_for_unit("network.target")
# Ensure the cluster comes back up again.
# FIXME: dmcrypt OSDs currently do not work out of the box.
# For a potential long-term fix see: https://github.com/NixOS/nixpkgs/pull/512912#discussion_r3140295546
osd1.succeed(
"ceph-volume lvm activate --no-tmpfs --no-systemd ${cfg.osd1.name} ${cfg.osd1.uuid}",
"systemctl start ceph-osd-${cfg.osd1.name}",
)
# Test the cluster state thoroughly.
monA.wait_until_succeeds("ceph -s | grep 'mon: 1 daemons'")
monA.wait_until_succeeds("ceph -s | grep 'quorum ${cfg.monA.name}'")
monA.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
monA.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
# Verify the recovery.
@@ -444,45 +545,50 @@ let
# Create a CephFS.
monA.succeed(
"ceph osd pool create cephfs-data 32 32",
"ceph osd pool create cephfs-metadata 32 32",
"ceph fs new cephfs cephfs-metadata cephfs-data",
"ceph fs volume create testing",
"ceph osd pool set cephfs.testing.data pg_num 32",
"ceph osd pool set cephfs.testing.meta pg_num 32",
)
# Wait for the MDS to claim the filesystem and become active.
monA.wait_until_succeeds("ceph fs status cephfs | grep -e 'active'", timeout=60)
monA.wait_until_succeeds("ceph fs status testing | grep -e 'active'", timeout=60)
# Distribute the admin keyring (and a plain secret file for the kernel
# client) to both client machines, so that they can authenticate.
# Create a subvolume, issue credentials, then distribute those credentials.
monA.succeed(
"cp /etc/ceph/ceph.client.admin.keyring /tmp/shared",
"ceph-authtool -p /etc/ceph/ceph.client.admin.keyring > /tmp/shared/admin.secret",
"ceph fs subvolumegroup create testing group",
"ceph fs subvolume create testing subvolume --group_name group",
"ceph fs subvolume authorize testing subvolume kclient group",
"ceph fs subvolume authorize testing subvolume fuseclient group",
"ceph auth get client.kclient -o /tmp/shared/ceph.client.kclient.keyring",
"ceph auth get client.fuseclient -o /tmp/shared/ceph.client.fuseclient.keyring",
)
kclient.succeed("cp /tmp/shared/ceph.client.admin.keyring /etc/ceph")
fuseclient.succeed("cp /tmp/shared/ceph.client.admin.keyring /etc/ceph")
kclient.succeed("cp /tmp/shared/admin.secret /etc/ceph/admin.secret")
kclient.succeed("cp /tmp/shared/ceph.client.kclient.keyring /etc/ceph")
fuseclient.succeed("cp /tmp/shared/ceph.client.fuseclient.keyring /etc/ceph")
# Get the volume path generated by Ceph.
volume_path = monA.succeed("ceph fs subvolume getpath testing subvolume group | tee /dev/stderr").strip()
# Mount CephFS on the kernel client.
# We force the messenger v2 protocol via "ms_mode=secure"; the cluster
# has msgr2 enabled (see "ceph mon enable-msgr2" above) and the legacy v1
# has msgr2 enabled (the monmap is created with a v2 address above) and the legacy v1
# protocol apparently does not reconnect reliably after the servers are restarted.
# The msgr2 monitor listens on port 3300 (instead of legacy v1 port 6789),
# so we have to point the device string at that port explicitly.
# `recover_session=clean` makes the kernel client automatically reconnect
# (discarding its stale session) after the whole cluster has been down,
# which would otherwise leave the mount blocklisted and hanging forever.
# which would otherwise leave the mount blocklisted and hanging.
# Real CephFS use may not prefer hanging `recover_session=clean`, and
# prefer manual de-blocklisting to avoid any failed OS syscalls,
# but for this test, discarding stale sessions is good enough.
kclient.succeed("mkdir -p /mnt/cephfs")
kclient.wait_until_succeeds(
"mount -t ceph ${cfg.monA.ip}:3300:/ /mnt/cephfs -o name=admin,secretfile=/etc/ceph/admin.secret,ms_mode=secure,recover_session=clean"
f"mount -t ceph kclient@.testing={volume_path} /mnt/cephfs -o ms_mode=secure,recover_session=clean"
)
kclient.succeed("mountpoint /mnt/cephfs")
# Mount CephFS on the FUSE client using ceph-fuse.
fuseclient.succeed("mkdir -p /mnt/cephfs")
fuseclient.wait_until_succeeds(
"ceph-fuse --id admin -m ${cfg.monA.ip}:6789 /mnt/cephfs"
f"ceph-fuse --id fuseclient -m ${cfg.monA.ip}:3300 -r {volume_path} /mnt/cephfs"
)
fuseclient.succeed("mountpoint /mnt/cephfs")
@@ -510,24 +616,40 @@ let
osd1.crash()
osd2.crash()
# Start it up
# Start the mon first and mark the OSDs as down.
# Since the heartbeats are pretty high by default, the OSDs would otherwise be marked as up still.
# However we do not want to lower the heartbeats since this might cause flakey tests.
monA.start()
monA.wait_for_unit("ceph-mon-${cfg.monA.name}")
monA.wait_until_succeeds("ceph osd down all")
# Then start the OSDs as normal.
osd0.start()
osd1.start()
osd2.start()
monA.start()
# Ensure they are all up.
osd0.wait_for_unit("network.target")
osd1.wait_for_unit("network.target")
osd2.wait_for_unit("network.target")
# FIXME: dmcrypt OSDs currently do not work out of the box.
# For a potential long-term fix see: https://github.com/NixOS/nixpkgs/pull/512912#discussion_r3140295546
osd1.succeed(
"ceph-volume lvm activate --no-tmpfs --no-systemd ${cfg.osd1.name} ${cfg.osd1.uuid}",
"systemctl start ceph-osd-${cfg.osd1.name}",
)
# Ensure the cluster comes back up again.
# See the note above on why this uses `wait_until_succeeds`.
monA.wait_until_succeeds("ceph -s | grep 'mon: 1 daemons'")
monA.wait_until_succeeds("ceph -s | grep 'quorum ${cfg.monA.name}'")
monA.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
monA.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'", timeout=60)
# Ensure the MDS/CephFS comes back up again, too.
monA.wait_for_unit("ceph-mds-${cfg.monA.name}")
monA.wait_until_succeeds("ceph fs status cephfs | grep -e 'active'", timeout=60)
monA.wait_until_succeeds("ceph fs status testing | grep -e 'active'", timeout=60)
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
# The clients kept running across the outage, so their CephFS mounts

View File

@@ -1,291 +0,0 @@
# Tests the legacy FileStore OSD backend.
{ lib, ... }:
let
cfg = {
clusterId = "066ae264-2a5d-4729-8001-6ad265f50b03";
monA = {
name = "a";
ip = "192.168.1.1";
};
osd0 = {
name = "0";
ip = "192.168.1.2";
key = "AQBCEJNa3s8nHRAANvdsr93KqzBznuIWm2gOGg==";
uuid = "55ba2294-3e24-478f-bee0-9dca4c231dd9";
};
osd1 = {
name = "1";
ip = "192.168.1.3";
key = "AQBEEJNac00kExAAXEgy943BGyOpVH1LLlHafQ==";
uuid = "5e97a838-85b6-43b0-8950-cb56d554d1e5";
};
osd2 = {
name = "2";
ip = "192.168.1.4";
key = "AQAdyhZeIaUlARAAGRoidDAmS6Vkp546UFEf5w==";
uuid = "ea999274-13d0-4dd5-9af9-ad25a324f72f";
};
};
generateCephConfig =
{ daemonConfig }:
{
enable = true;
global = {
fsid = cfg.clusterId;
monHost = cfg.monA.ip;
monInitialMembers = cfg.monA.name;
};
}
// daemonConfig;
generateHost =
{ cephConfig, networkConfig }:
{ pkgs, ... }:
{
virtualisation = {
emptyDiskImages = [ 20480 ];
vlans = [ 1 ];
};
networking = networkConfig;
environment.systemPackages = with pkgs; [
bash
sudo
ceph
xfsprogs
netcat
];
boot.kernelModules = [ "xfs" ];
services.ceph = cephConfig;
};
networkMonA = {
dhcpcd.enable = false;
interfaces.eth1.ipv4.addresses = lib.mkOverride 0 [
{
address = cfg.monA.ip;
prefixLength = 24;
}
];
firewall = {
allowedTCPPorts = [
6789
3300
];
allowedTCPPortRanges = [
{
from = 6800;
to = 7300;
}
];
};
};
cephConfigMonA = generateCephConfig {
daemonConfig = {
mon = {
enable = true;
daemons = [ cfg.monA.name ];
};
mgr = {
enable = true;
daemons = [ cfg.monA.name ];
};
};
};
networkOsd = osd: {
dhcpcd.enable = false;
interfaces.eth1.ipv4.addresses = lib.mkOverride 0 [
{
address = osd.ip;
prefixLength = 24;
}
];
firewall = {
allowedTCPPortRanges = [
{
from = 6800;
to = 7300;
}
];
};
};
cephConfigOsd =
osd:
generateCephConfig {
daemonConfig = {
osd = {
enable = true;
daemons = [ osd.name ];
};
};
};
# Following deployment is based on the manual deployment described here:
# https://docs.ceph.com/docs/master/install/manual-deployment/
# For other ways to deploy a ceph cluster, look at the documentation at
# https://docs.ceph.com/docs/master/
testscript =
{ ... }:
''
start_all()
monA.wait_for_unit("network.target")
osd0.wait_for_unit("network.target")
osd1.wait_for_unit("network.target")
osd2.wait_for_unit("network.target")
# Bootstrap ceph-mon daemon
monA.succeed(
"sudo -u ceph ceph-authtool --create-keyring /tmp/ceph.mon.keyring --gen-key -n mon. --cap mon 'allow *'",
"sudo -u ceph ceph-authtool --create-keyring /etc/ceph/ceph.client.admin.keyring --gen-key -n client.admin --cap mon 'allow *' --cap osd 'allow *' --cap mds 'allow *' --cap mgr 'allow *'",
"sudo -u ceph ceph-authtool /tmp/ceph.mon.keyring --import-keyring /etc/ceph/ceph.client.admin.keyring",
"monmaptool --create --add ${cfg.monA.name} ${cfg.monA.ip} --fsid ${cfg.clusterId} /tmp/monmap",
"sudo -u ceph ceph-mon --mkfs -i ${cfg.monA.name} --monmap /tmp/monmap --keyring /tmp/ceph.mon.keyring",
"sudo -u ceph mkdir -p /var/lib/ceph/mgr/ceph-${cfg.monA.name}/",
"sudo -u ceph touch /var/lib/ceph/mon/ceph-${cfg.monA.name}/done",
"systemctl start ceph-mon-${cfg.monA.name}",
)
monA.wait_for_unit("ceph-mon-${cfg.monA.name}")
monA.succeed("ceph mon enable-msgr2")
monA.succeed("ceph config set mon auth_allow_insecure_global_id_reclaim false")
# Can't check ceph status until a mon is up
monA.succeed("ceph -s | grep 'mon: 1 daemons'")
# Start the ceph-mgr daemon, it has no deps and hardly any setup
monA.succeed(
"ceph auth get-or-create mgr.${cfg.monA.name} mon 'allow profile mgr' osd 'allow *' mds 'allow *' > /var/lib/ceph/mgr/ceph-${cfg.monA.name}/keyring",
"sync", # to ensure shell redirection above is durable
"systemctl start ceph-mgr-${cfg.monA.name}",
)
monA.wait_for_unit("ceph-mgr-a")
monA.wait_until_succeeds("ceph -s | grep 'quorum ${cfg.monA.name}'")
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
# Send the admin keyring to the OSD machines
monA.succeed("cp /etc/ceph/ceph.client.admin.keyring /tmp/shared")
osd0.succeed("cp /tmp/shared/ceph.client.admin.keyring /etc/ceph")
osd1.succeed("cp /tmp/shared/ceph.client.admin.keyring /etc/ceph")
osd2.succeed("cp /tmp/shared/ceph.client.admin.keyring /etc/ceph")
# Bootstrap OSDs
osd0.succeed(
"mkfs.xfs /dev/vdb",
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd0.name}",
"mount /dev/vdb /var/lib/ceph/osd/ceph-${cfg.osd0.name}",
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd0.name}/keyring --name osd.${cfg.osd0.name} --add-key ${cfg.osd0.key}",
'echo \'{"cephx_secret": "${cfg.osd0.key}"}\' | ceph osd new ${cfg.osd0.uuid} -i -',
)
osd1.succeed(
"mkfs.xfs /dev/vdb",
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd1.name}",
"mount /dev/vdb /var/lib/ceph/osd/ceph-${cfg.osd1.name}",
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd1.name}/keyring --name osd.${cfg.osd1.name} --add-key ${cfg.osd1.key}",
'echo \'{"cephx_secret": "${cfg.osd1.key}"}\' | ceph osd new ${cfg.osd1.uuid} -i -',
)
osd2.succeed(
"mkfs.xfs /dev/vdb",
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd2.name}",
"mount /dev/vdb /var/lib/ceph/osd/ceph-${cfg.osd2.name}",
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd2.name}/keyring --name osd.${cfg.osd2.name} --add-key ${cfg.osd2.key}",
'echo \'{"cephx_secret": "${cfg.osd2.key}"}\' | ceph osd new ${cfg.osd2.uuid} -i -',
)
# We `sync` so that the config survives the forced crashes below.
osd0.succeed(
"ceph-osd -i ${cfg.osd0.name} --mkfs --osd-uuid ${cfg.osd0.uuid}",
"chown -R ceph:ceph /var/lib/ceph/osd",
"sync",
"systemctl start ceph-osd-${cfg.osd0.name}",
)
osd1.succeed(
"ceph-osd -i ${cfg.osd1.name} --mkfs --osd-uuid ${cfg.osd1.uuid}",
"chown -R ceph:ceph /var/lib/ceph/osd",
"sync",
"systemctl start ceph-osd-${cfg.osd1.name}",
)
osd2.succeed(
"ceph-osd -i ${cfg.osd2.name} --mkfs --osd-uuid ${cfg.osd2.uuid}",
"chown -R ceph:ceph /var/lib/ceph/osd",
"sync",
"systemctl start ceph-osd-${cfg.osd2.name}",
)
monA.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
monA.succeed(
"ceph osd pool create multi-node-test 32 32",
"ceph osd pool ls | grep 'multi-node-test'",
# We need to enable an application on the pool, otherwise it will
# stay unhealthy in state POOL_APP_NOT_ENABLED.
# Creating a CephFS would do this automatically, but we haven't done that here.
# See: https://docs.ceph.com/en/reef/rados/operations/pools/#associating-a-pool-with-an-application
# We use the custom application name "nixos-test" for this.
"ceph osd pool application enable multi-node-test nixos-test",
"ceph osd pool rename multi-node-test multi-node-other-test",
"ceph osd pool ls | grep 'multi-node-other-test'",
)
monA.wait_until_succeeds("ceph -s | grep '2 pools, 33 pgs'")
monA.succeed("ceph osd pool set multi-node-other-test size 2")
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
monA.wait_until_succeeds("ceph -s | grep '33 active+clean'")
monA.fail(
"ceph osd pool ls | grep 'multi-node-test'",
"ceph osd pool delete multi-node-other-test multi-node-other-test --yes-i-really-really-mean-it",
)
# Shut down ceph on all machines in a very unpolite way
monA.crash()
osd0.crash()
osd1.crash()
osd2.crash()
# Start it up
osd0.start()
osd1.start()
osd2.start()
monA.start()
# Ensure the cluster comes back up again
monA.succeed("ceph -s | grep 'mon: 1 daemons'")
monA.wait_until_succeeds("ceph -s | grep 'quorum ${cfg.monA.name}'")
monA.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
'';
in
{
name = "basic-multi-node-ceph-cluster-deprecated-filestore";
meta = with lib.maintainers; {
maintainers = [ lejonet ];
};
nodes = {
monA = generateHost {
cephConfig = cephConfigMonA;
networkConfig = networkMonA;
};
osd0 = generateHost {
cephConfig = cephConfigOsd cfg.osd0;
networkConfig = networkOsd cfg.osd0;
};
osd1 = generateHost {
cephConfig = cephConfigOsd cfg.osd1;
networkConfig = networkOsd cfg.osd1;
};
osd2 = generateHost {
cephConfig = cephConfigOsd cfg.osd2;
networkConfig = networkOsd cfg.osd2;
};
};
testScript = testscript;
}

View File

@@ -1,269 +0,0 @@
{ lib, ... }:
let
# the single node ipv6 address
ip = "2001:db8:ffff::";
# the global ceph cluster id
cluster = "54465b37-b9d8-4539-a1f9-dd33c75ee45a";
# the fsids of OSDs
osd-fsid-map = {
"0" = "1c1b7ea9-06bf-4d30-9a01-37ac3a0254aa";
"1" = "bd5a6f49-69d5-428c-ac25-a99f0c44375c";
"2" = "c90de6c7-86c6-41da-9694-e794096dfc5c";
};
in
{
name = "basic-single-node-ceph-cluster-bluestore-dmcrypt";
meta.maintainers = with lib.maintainers; [
benaryorg
nh2
];
nodes.ceph =
{
lib,
pkgs,
config,
...
}:
{
# disks for bluestore
virtualisation.emptyDiskImages = [
20480
20480
20480
];
# networking setup (no external connectivity required, only local IPv6)
networking.useDHCP = false;
systemd.network = {
enable = true;
wait-online.extraArgs = [
"-i"
"lo"
];
networks = {
"40-loopback" = {
enable = true;
name = "lo";
DHCP = "no";
addresses = [ { Address = "${ip}/128"; } ];
};
};
};
# do not start the ceph target by default so we can format the disks first
systemd.targets.ceph.wantedBy = lib.mkForce [ ];
# add the packages to systemPackages so the testscript doesn't run into any unexpected issues
# this shouldn't be required on production systems which have their required packages in the unit paths only
# but it helps in case one needs to actually run the tooling anyway
environment.systemPackages = with pkgs; [
ceph
cryptsetup
lvm2
];
services.ceph = {
enable = true;
client.enable = true;
extraConfig = {
public_addr = ip;
cluster_addr = ip;
# ipv6
ms_bind_ipv4 = "false";
ms_bind_ipv6 = "true";
# msgr2 settings
ms_cluster_mode = "secure";
ms_service_mode = "secure";
ms_client_mode = "secure";
ms_mon_cluster_mode = "secure";
ms_mon_service_mode = "secure";
ms_mon_client_mode = "secure";
# less default modules, cuts down on memory and startup time in the tests
mgr_initial_modules = "";
# distribute by OSD, not by host, as per https://docs.ceph.com/en/reef/cephadm/install/#single-host
osd_crush_chooseleaf_type = "0";
};
client.extraConfig."mon.0" = {
host = "ceph";
mon_addr = "v2:[${ip}]:3300";
public_addr = "v2:[${ip}]:3300";
};
global = {
fsid = cluster;
clusterNetwork = "${ip}/64";
publicNetwork = "${ip}/64";
monInitialMembers = "0";
};
mon = {
enable = true;
daemons = [ "0" ];
};
osd = {
enable = true;
daemons = builtins.attrNames osd-fsid-map;
};
mgr = {
enable = true;
daemons = [ "ceph" ];
};
};
systemd.services =
let
osd-name = id: "ceph-osd-${id}";
osd-pre-start = id: [
"!${config.services.ceph.osd.package.out}/bin/ceph-volume lvm activate --bluestore ${id} ${osd-fsid-map.${id}} --no-systemd"
"${config.services.ceph.osd.package.lib}/libexec/ceph/ceph-osd-prestart.sh --id ${id} --cluster ${config.services.ceph.global.clusterName}"
];
osd-post-stop = id: [
"!${config.services.ceph.osd.package.out}/bin/ceph-volume lvm deactivate ${id}"
];
map-osd = id: {
name = osd-name id;
value = {
serviceConfig.ExecStartPre = lib.mkForce (osd-pre-start id);
serviceConfig.ExecStopPost = osd-post-stop id;
unitConfig.ConditionPathExists = lib.mkForce [ ];
unitConfig.StartLimitBurst = lib.mkForce 4;
path = with pkgs; [
util-linux
lvm2
cryptsetup
];
};
};
in
lib.pipe config.services.ceph.osd.daemons [
(map map-osd)
builtins.listToAttrs
];
};
testScript = ''
start_all()
ceph.wait_for_unit("default.target")
# Bootstrap ceph-mon daemon
ceph.succeed(
"mkdir -p /var/lib/ceph/bootstrap-osd",
"ceph-authtool --create-keyring /tmp/ceph.mon.keyring --gen-key -n mon. --cap mon 'allow *'",
"ceph-authtool --create-keyring /etc/ceph/ceph.client.admin.keyring --gen-key -n client.admin --cap mon 'allow *' --cap osd 'allow *' --cap mds 'allow *' --cap mgr 'allow *'",
"ceph-authtool --create-keyring /var/lib/ceph/bootstrap-osd/ceph.keyring --gen-key -n client.bootstrap-osd --cap mon 'profile bootstrap-osd' --cap mgr 'allow r'",
"ceph-authtool /tmp/ceph.mon.keyring --import-keyring /etc/ceph/ceph.client.admin.keyring",
"ceph-authtool /tmp/ceph.mon.keyring --import-keyring /var/lib/ceph/bootstrap-osd/ceph.keyring",
"monmaptool --create --fsid ${cluster} --addv 0 'v2:[${ip}]:3300/0' --clobber /tmp/ceph.initial-monmap",
"mkdir -p /var/lib/ceph/mon/ceph-0",
"ceph-mon --mkfs -i 0 --monmap /tmp/ceph.initial-monmap --keyring /tmp/ceph.mon.keyring",
"chown ceph:ceph -R /tmp/ceph.mon.keyring /var/lib/ceph",
"systemctl start ceph-mon-0.service",
)
ceph.wait_for_unit("ceph-mon-0.service")
# should the mon not start or bind for some reason this gives us a better error message than the config commands running into a timeout
ceph.wait_for_open_port(3300, "${ip}")
ceph.succeed(
# required for HEALTH_OK
"ceph config set mon auth_allow_insecure_global_id_reclaim false",
# IPv6
"ceph config set global ms_bind_ipv4 false",
"ceph config set global ms_bind_ipv6 true",
# the new (secure) protocol
"ceph config set global ms_bind_msgr1 false",
"ceph config set global ms_bind_msgr2 true",
# just a small little thing
"ceph config set mon mon_compact_on_start true",
)
# Can't check ceph status until a mon is up
ceph.succeed("ceph -s | grep 'mon: 1 daemons'")
# Bootstrap OSDs (do this before starting the mgr because cryptsetup and the mgr both eat a lot of memory)
ceph.succeed(
# this will automatically do what's required for LVM, cryptsetup, and stores all the data in Ceph's internal databases
"ceph-volume lvm prepare --bluestore --data /dev/vdb --dmcrypt --no-systemd --osd-id 0 --osd-fsid ${osd-fsid-map."0"}",
"ceph-volume lvm prepare --bluestore --data /dev/vdc --dmcrypt --no-systemd --osd-id 1 --osd-fsid ${osd-fsid-map."1"}",
"ceph-volume lvm prepare --bluestore --data /dev/vdd --dmcrypt --no-systemd --osd-id 2 --osd-fsid ${osd-fsid-map."2"}",
"sudo ceph-volume lvm deactivate 0",
"sudo ceph-volume lvm deactivate 1",
"sudo ceph-volume lvm deactivate 2",
"chown -R ceph:ceph /var/lib/ceph",
)
# Start OSDs (again, argon2id eats memory, so this happens before starting the mgr)
ceph.succeed(
"systemctl start ceph-osd-0.service",
"systemctl start ceph-osd-1.service",
"systemctl start ceph-osd-2.service",
)
ceph.wait_until_succeeds("ceph -s | grep 'quorum 0'")
ceph.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
# Start the ceph-mgr daemon, after copying in the keyring
ceph.succeed(
"mkdir -p /var/lib/ceph/mgr/ceph-ceph/",
"ceph auth get-or-create -o /var/lib/ceph/mgr/ceph-ceph/keyring mgr.ceph mon 'allow profile mgr' osd 'allow *' mds 'allow *'",
"chown -R ceph:ceph /var/lib/ceph/mgr/ceph-ceph/",
"systemctl start ceph-mgr-ceph.service",
)
ceph.wait_for_unit("ceph-mgr-ceph")
ceph.wait_until_succeeds("ceph -s | grep 'quorum 0'")
ceph.wait_until_succeeds("ceph -s | grep 'mgr: ceph(active,'")
ceph.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
ceph.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
# test the actual storage
ceph.succeed(
"ceph osd pool create single-node-test 32 32",
"ceph osd pool ls | grep 'single-node-test'",
# We need to enable an application on the pool, otherwise it will
# stay unhealthy in state POOL_APP_NOT_ENABLED.
# Creating a CephFS would do this automatically, but we haven't done that here.
# See: https://docs.ceph.com/en/reef/rados/operations/pools/#associating-a-pool-with-an-application
# We use the custom application name "nixos-test" for this.
"ceph osd pool application enable single-node-test nixos-test",
"ceph osd pool rename single-node-test single-node-other-test",
"ceph osd pool ls | grep 'single-node-other-test'",
)
ceph.wait_until_succeeds("ceph -s | grep '2 pools, 33 pgs'")
ceph.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
ceph.wait_until_succeeds("ceph -s | grep '33 active+clean'")
ceph.fail(
# the old pool should be gone
"ceph osd pool ls | grep 'multi-node-test'",
# deleting the pool should fail without setting mon_allow_pool_delete
"ceph osd pool delete single-node-other-test single-node-other-test --yes-i-really-really-mean-it",
)
# rebooting gets rid of any potential tmpfs mounts or device-mapper devices
ceph.shutdown()
ceph.start()
ceph.wait_for_unit("default.target")
# Start it up (again OSDs first due to memory constraints of cryptsetup and mgr)
ceph.systemctl("start ceph-mon-0.service")
ceph.wait_for_unit("ceph-mon-0")
ceph.systemctl("start ceph-osd-0.service")
ceph.wait_for_unit("ceph-osd-0")
ceph.systemctl("start ceph-osd-1.service")
ceph.wait_for_unit("ceph-osd-1")
ceph.systemctl("start ceph-osd-2.service")
ceph.wait_for_unit("ceph-osd-2")
ceph.systemctl("start ceph-mgr-ceph.service")
ceph.wait_for_unit("ceph-mgr-ceph")
# Ensure the cluster comes back up again
ceph.succeed("ceph -s | grep 'mon: 1 daemons'")
ceph.wait_until_succeeds("ceph -s | grep 'quorum 0'")
ceph.wait_until_succeeds("ceph osd stat | grep -E '3 osds: 3 up[^,]*, 3 in'")
ceph.wait_until_succeeds("ceph -s | grep 'mgr: ceph(active,'")
ceph.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
'';
}

View File

@@ -9,17 +9,14 @@ let
};
osd0 = {
name = "0";
key = "AQBCEJNa3s8nHRAANvdsr93KqzBznuIWm2gOGg==";
uuid = "55ba2294-3e24-478f-bee0-9dca4c231dd9";
};
osd1 = {
name = "1";
key = "AQBEEJNac00kExAAXEgy943BGyOpVH1LLlHafQ==";
uuid = "5e97a838-85b6-43b0-8950-cb56d554d1e5";
};
osd2 = {
name = "2";
key = "AQAdyhZeIaUlARAAGRoidDAmS6Vkp546UFEf5w==";
uuid = "ea999274-13d0-4dd5-9af9-ad25a324f72f";
};
};
@@ -51,6 +48,11 @@ let
vlans = [ 1 ];
};
# Ceph 20.2.4 introduced the aes256k cipher for authentication.
# Linux started supporting these in kernel version 7.0.
# Remove this line at the earliest convenience (i.e. when tests are run by 7.0 or higher by default).
boot.kernelPackages = pkgs.linuxPackages_latest;
networking = networkConfig;
environment.systemPackages = with pkgs; [
@@ -115,13 +117,18 @@ let
"sudo -u ceph ceph-authtool --create-keyring /tmp/ceph.mon.keyring --gen-key -n mon. --cap mon 'allow *'",
"sudo -u ceph ceph-authtool --create-keyring /etc/ceph/ceph.client.admin.keyring --gen-key -n client.admin --cap mon 'allow *' --cap osd 'allow *' --cap mds 'allow *' --cap mgr 'allow *'",
"sudo -u ceph ceph-authtool /tmp/ceph.mon.keyring --import-keyring /etc/ceph/ceph.client.admin.keyring",
"monmaptool --create --add ${cfg.monA.name} ${cfg.monA.ip} --fsid ${cfg.clusterId} /tmp/monmap",
# Create the monmap with both a msgr2 (v2) and a legacy (v1) address.
# Using plain `--add` yields a v1-only monmap, which leaves the cluster
# in HEALTH_WARN with MON_MSGR2_NOT_ENABLED. Running `ceph mon
# enable-msgr2` afterwards is not enough: it rewrites the monmap (a
# subsequent `ceph mon dump` does show the v2 address), but the health
# check keeps reporting the mon as v1-only indefinitely.
"monmaptool --create --addv ${cfg.monA.name} '[v2:${cfg.monA.ip}:3300,v1:${cfg.monA.ip}:6789]' --fsid ${cfg.clusterId} /tmp/monmap",
"sudo -u ceph ceph-mon --mkfs -i ${cfg.monA.name} --monmap /tmp/monmap --keyring /tmp/ceph.mon.keyring",
"sudo -u ceph touch /var/lib/ceph/mon/ceph-${cfg.monA.name}/done",
"systemctl start ceph-mon-${cfg.monA.name}",
)
monA.wait_for_unit("ceph-mon-${cfg.monA.name}")
monA.succeed("ceph mon enable-msgr2")
monA.succeed("ceph config set mon auth_allow_insecure_global_id_reclaim false")
# Can't check ceph status until a mon is up
@@ -148,14 +155,24 @@ let
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd2.name}",
"echo bluestore > /var/lib/ceph/osd/ceph-${cfg.osd2.name}/type",
"ln -sf /dev/vdd /var/lib/ceph/osd/ceph-${cfg.osd2.name}/block",
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd0.name}/keyring --name osd.${cfg.osd0.name} --add-key ${cfg.osd0.key}",
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd1.name}/keyring --name osd.${cfg.osd1.name} --add-key ${cfg.osd1.key}",
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd2.name}/keyring --name osd.${cfg.osd2.name} --add-key ${cfg.osd2.key}",
'echo \'{"cephx_secret": "${cfg.osd0.key}"}\' | ceph osd new ${cfg.osd0.uuid} -i -',
'echo \'{"cephx_secret": "${cfg.osd1.key}"}\' | ceph osd new ${cfg.osd1.uuid} -i -',
'echo \'{"cephx_secret": "${cfg.osd2.key}"}\' | ceph osd new ${cfg.osd2.uuid} -i -',
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd0.name}/keyring --name osd.${cfg.osd0.name} --gen-key",
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd1.name}/keyring --name osd.${cfg.osd1.name} --gen-key",
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd2.name}/keyring --name osd.${cfg.osd2.name} --gen-key",
)
# Register the OSDs with the generated keys read back from their keyrings.
for osd_name, osd_uuid in [
("${cfg.osd0.name}", "${cfg.osd0.uuid}"),
("${cfg.osd1.name}", "${cfg.osd1.uuid}"),
("${cfg.osd2.name}", "${cfg.osd2.uuid}"),
]:
key = monA.succeed(
f"ceph-authtool --print-key /var/lib/ceph/osd/ceph-{osd_name}/keyring --name osd.{osd_name}"
).strip()
monA.succeed(
f"echo '{{\"cephx_secret\": \"{key}\"}}' | ceph osd new {osd_uuid} -i -"
)
# Initialize the OSDs with regular filestore
monA.succeed(
"ceph-osd -i ${cfg.osd0.name} --mkfs --osd-uuid ${cfg.osd0.uuid}",

View File

@@ -1,249 +0,0 @@
{ lib, ... }:
let
cfg = {
clusterId = "066ae264-2a5d-4729-8001-6ad265f50b03";
monA = {
name = "a";
ip = "192.168.1.1";
};
osd0 = {
name = "0";
key = "AQBCEJNa3s8nHRAANvdsr93KqzBznuIWm2gOGg==";
uuid = "55ba2294-3e24-478f-bee0-9dca4c231dd9";
};
osd1 = {
name = "1";
key = "AQBEEJNac00kExAAXEgy943BGyOpVH1LLlHafQ==";
uuid = "5e97a838-85b6-43b0-8950-cb56d554d1e5";
};
osd2 = {
name = "2";
key = "AQAdyhZeIaUlARAAGRoidDAmS6Vkp546UFEf5w==";
uuid = "ea999274-13d0-4dd5-9af9-ad25a324f72f";
};
};
generateCephConfig =
{ daemonConfig }:
{
enable = true;
global = {
fsid = cfg.clusterId;
monHost = cfg.monA.ip;
monInitialMembers = cfg.monA.name;
};
}
// daemonConfig;
generateHost =
{
cephConfig,
networkConfig,
}:
{ pkgs, ... }:
{
virtualisation = {
memorySize = 2048;
emptyDiskImages = [
20480
20480
20480
];
vlans = [ 1 ];
};
networking = networkConfig;
environment.systemPackages = with pkgs; [
bash
sudo
ceph
xfsprogs
];
boot.kernelModules = [ "xfs" ];
services.ceph = cephConfig;
};
networkMonA = {
dhcpcd.enable = false;
interfaces.eth1.ipv4.addresses = lib.mkOverride 0 [
{
address = cfg.monA.ip;
prefixLength = 24;
}
];
};
cephConfigMonA = generateCephConfig {
daemonConfig = {
mon = {
enable = true;
daemons = [ cfg.monA.name ];
};
mgr = {
enable = true;
daemons = [ cfg.monA.name ];
};
osd = {
enable = true;
daemons = [
cfg.osd0.name
cfg.osd1.name
cfg.osd2.name
];
};
rgw = {
enable = true;
daemons = [ cfg.monA.name ];
};
};
};
# Following deployment is based on the manual deployment described here:
# https://docs.ceph.com/docs/master/install/manual-deployment/
# For other ways to deploy a ceph cluster, look at the documentation at
# https://docs.ceph.com/docs/master/
testScript = ''
start_all()
monA.wait_for_unit("network.target")
# Bootstrap ceph-mon daemon
monA.succeed(
"sudo -u ceph ceph-authtool --create-keyring /tmp/ceph.mon.keyring --gen-key -n mon. --cap mon 'allow *'",
"sudo -u ceph ceph-authtool --create-keyring /etc/ceph/ceph.client.admin.keyring --gen-key -n client.admin --cap mon 'allow *' --cap osd 'allow *' --cap mds 'allow *' --cap mgr 'allow *'",
"sudo -u ceph ceph-authtool /tmp/ceph.mon.keyring --import-keyring /etc/ceph/ceph.client.admin.keyring",
"monmaptool --create --add ${cfg.monA.name} ${cfg.monA.ip} --fsid ${cfg.clusterId} /tmp/monmap",
"sudo -u ceph ceph-mon --mkfs -i ${cfg.monA.name} --monmap /tmp/monmap --keyring /tmp/ceph.mon.keyring",
"sudo -u ceph touch /var/lib/ceph/mon/ceph-${cfg.monA.name}/done",
"systemctl start ceph-mon-${cfg.monA.name}",
)
monA.wait_for_unit("ceph-mon-${cfg.monA.name}")
monA.succeed("ceph mon enable-msgr2")
monA.succeed("ceph config set mon auth_allow_insecure_global_id_reclaim false")
# Can't check ceph status until a mon is up
monA.succeed("ceph -s | grep 'mon: 1 daemons'")
# Start the ceph-mgr daemon, after copying in the keyring
monA.succeed(
"sudo -u ceph mkdir -p /var/lib/ceph/mgr/ceph-${cfg.monA.name}/",
"ceph auth get-or-create mgr.${cfg.monA.name} mon 'allow profile mgr' osd 'allow *' mds 'allow *' > /var/lib/ceph/mgr/ceph-${cfg.monA.name}/keyring",
"systemctl start ceph-mgr-${cfg.monA.name}",
)
monA.wait_for_unit("ceph-mgr-a")
monA.wait_until_succeeds("ceph -s | grep 'quorum ${cfg.monA.name}'")
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
# Bootstrap OSDs
monA.succeed(
"mkfs.xfs /dev/vdb",
"mkfs.xfs /dev/vdc",
"mkfs.xfs /dev/vdd",
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd0.name}",
"mount /dev/vdb /var/lib/ceph/osd/ceph-${cfg.osd0.name}",
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd1.name}",
"mount /dev/vdc /var/lib/ceph/osd/ceph-${cfg.osd1.name}",
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd2.name}",
"mount /dev/vdd /var/lib/ceph/osd/ceph-${cfg.osd2.name}",
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd0.name}/keyring --name osd.${cfg.osd0.name} --add-key ${cfg.osd0.key}",
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd1.name}/keyring --name osd.${cfg.osd1.name} --add-key ${cfg.osd1.key}",
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd2.name}/keyring --name osd.${cfg.osd2.name} --add-key ${cfg.osd2.key}",
'echo \'{"cephx_secret": "${cfg.osd0.key}"}\' | ceph osd new ${cfg.osd0.uuid} -i -',
'echo \'{"cephx_secret": "${cfg.osd1.key}"}\' | ceph osd new ${cfg.osd1.uuid} -i -',
'echo \'{"cephx_secret": "${cfg.osd2.key}"}\' | ceph osd new ${cfg.osd2.uuid} -i -',
)
# Initialize the OSDs with regular filestore
monA.succeed(
"ceph-osd -i ${cfg.osd0.name} --mkfs --osd-uuid ${cfg.osd0.uuid}",
"ceph-osd -i ${cfg.osd1.name} --mkfs --osd-uuid ${cfg.osd1.uuid}",
"ceph-osd -i ${cfg.osd2.name} --mkfs --osd-uuid ${cfg.osd2.uuid}",
"chown -R ceph:ceph /var/lib/ceph/osd",
"systemctl start ceph-osd-${cfg.osd0.name}",
"systemctl start ceph-osd-${cfg.osd1.name}",
"systemctl start ceph-osd-${cfg.osd2.name}",
)
monA.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
monA.succeed(
"ceph osd pool create single-node-test 32 32",
"ceph osd pool ls | grep 'single-node-test'",
# We need to enable an application on the pool, otherwise it will
# stay unhealthy in state POOL_APP_NOT_ENABLED.
# Creating a CephFS would do this automatically, but we haven't done that here.
# See: https://docs.ceph.com/en/reef/rados/operations/pools/#associating-a-pool-with-an-application
# We use the custom application name "nixos-test" for this.
"ceph osd pool application enable single-node-test nixos-test",
"ceph osd pool rename single-node-test single-node-other-test",
"ceph osd pool ls | grep 'single-node-other-test'",
)
monA.wait_until_succeeds("ceph -s | grep '2 pools, 33 pgs'")
monA.succeed(
"ceph osd getcrushmap -o crush",
"crushtool -d crush -o decrushed",
"sed 's/step chooseleaf firstn 0 type host/step chooseleaf firstn 0 type osd/' decrushed > modcrush",
"crushtool -c modcrush -o recrushed",
"ceph osd setcrushmap -i recrushed",
"ceph osd pool set single-node-other-test size 2",
)
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
monA.wait_until_succeeds("ceph -s | grep '33 active+clean'")
monA.fail(
"ceph osd pool ls | grep 'multi-node-test'",
"ceph osd pool delete single-node-other-test single-node-other-test --yes-i-really-really-mean-it",
)
# Bootstrap RGW
monA.succeed(
"sudo -u ceph mkdir -p /var/lib/ceph/radosgw/ceph-${cfg.monA.name}",
"ceph auth get-or-create client.${cfg.monA.name} osd 'allow rwx' mon 'allow rw' > /var/lib/ceph/radosgw/ceph-${cfg.monA.name}/keyring",
"chown ceph:ceph /var/lib/ceph/radosgw/ceph-${cfg.monA.name}/keyring",
"systemctl start ceph-rgw-${cfg.monA.name}",
)
monA.wait_for_unit("ceph-rgw-${cfg.monA.name}")
monA.wait_for_open_port(7480)
# Shut down ceph by stopping ceph.target.
monA.succeed("systemctl stop ceph.target")
# Start it up
monA.succeed("systemctl start ceph.target")
monA.wait_for_unit("ceph-mon-${cfg.monA.name}")
monA.wait_for_unit("ceph-mgr-${cfg.monA.name}")
monA.wait_for_unit("ceph-osd-${cfg.osd0.name}")
monA.wait_for_unit("ceph-osd-${cfg.osd1.name}")
monA.wait_for_unit("ceph-osd-${cfg.osd2.name}")
monA.wait_for_unit("ceph-rgw-${cfg.monA.name}")
# Ensure the cluster comes back up again
monA.succeed("ceph -s | grep 'mon: 1 daemons'")
monA.wait_until_succeeds("ceph -s | grep 'quorum ${cfg.monA.name}'")
monA.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
'';
in
{
name = "basic-single-node-ceph-cluster-deprecated-filestore";
meta = with lib.maintainers; {
maintainers = [
lejonet
johanot
];
};
nodes = {
monA = generateHost {
cephConfig = cephConfigMonA;
networkConfig = networkMonA;
};
};
inherit testScript;
}

View File

@@ -2,7 +2,7 @@
{
name = "kexec";
meta = with lib.maintainers; {
maintainers = [
maintainers = pkgs.kexec-tools.meta.maintainers ++ [
flokli
lassulus
];

View File

@@ -36,13 +36,18 @@ in
enable = true;
settings = {
PORT = 10001;
DB_CONNECTION_STRING = "host=/run/postgresql user=${username} database=${username}";
DB_CONNECTION_STRING = "postgresql:///${username}?host=/run/postgresql";
};
credentials = {
inherit ENCRYPTION_KEY;
};
};
systemd.services.pocket-id = {
after = [ "postgresql.target" ];
requires = [ "postgresql.target" ];
};
services.postgresql = {
enable = true;
ensureUsers = [

View File

@@ -90,7 +90,6 @@ import ./make-test-python.nix (
# test server
machine.succeed("${qgisPackage}/bin/qgis_mapserver --version | grep 'QGIS ${qgisPackage.version}'")
machine.succeed("curl --head http://localhost | grep 'Server:.*${qgisPackage.version}'")
machine.succeed("curl http://localhost/index.json | grep 'Landing page as JSON'")
'';
}

View File

@@ -1,148 +1,197 @@
{ lib, pkgs, ... }:
let
genNodeId =
name:
pkgs.runCommand "syncthing-test-certs-${name}" { } ''
mkdir -p $out
${pkgs.syncthing}/bin/syncthing generate --home=$out
${pkgs.libxml2}/bin/xmllint --xpath 'string(configuration/device/@id)' $out/config.xml > $out/id
'';
idA = genNodeId "a";
idB = genNodeId "b";
idC = genNodeId "c";
testPassword = "it's a secret";
in
{
name = "syncthing";
meta.maintainers = with pkgs.lib.maintainers; [ zarelit ];
nodeNames = [
"a"
"b"
"c"
];
nodeDirs = lib.genAttrs nodeNames (n: ./test-nodes + "/${n}");
nodeData = lib.mapAttrs (n: v: {
cert = "${v}/cert.pem";
key = "${v}/key.pem";
id = lib.fileContents (v + "/id");
}) nodeDirs;
nodes = {
a =
{ config, ... }:
{
environment.etc.bar-encryption-password.text = testPassword;
services.syncthing = {
enable = true;
openDefaultPorts = true;
cert = "${idA}/cert.pem";
key = "${idA}/key.pem";
guiAddress = "unix:///run/syncthing/syncthing.sock";
settings = {
devices.b.id = lib.fileContents "${idB}/id";
devices.c.id = lib.fileContents "${idC}/id";
folders.foo = {
path = "/var/lib/syncthing/foo";
devices = [ "b" ];
};
folders.bar = {
path = "/var/lib/syncthing/bar";
devices = [
{
name = "c";
encryptionPasswordFile = "/etc/${config.environment.etc.bar-encryption-password.target}";
}
];
};
folders.baz = {
path = "/var/lib/syncthing/baz";
devices = [
"b"
"c"
];
ignorePatterns = [ ];
};
folders."foo bar" = {
path = "/var/lib/syncthing/foo-bar";
devices = [
"b"
];
};
};
};
};
b =
{ config, ... }:
{
environment.etc.bar-encryption-password.text = testPassword;
services.syncthing = {
enable = true;
openDefaultPorts = true;
cert = "${idB}/cert.pem";
key = "${idB}/key.pem";
settings = {
devices.a.id = lib.fileContents "${idA}/id";
devices.c.id = lib.fileContents "${idC}/id";
folders.foo = {
path = "/var/lib/syncthing/foo";
devices = [ "a" ];
};
folders.bar = {
path = "/var/lib/syncthing/bar";
devices = [
{
name = "c";
encryptionPasswordFile = "/etc/${config.environment.etc.bar-encryption-password.target}";
}
];
};
folders.baz = {
path = "/var/lib/syncthing/baz";
devices = [
"a"
"c"
];
ignorePatterns = [
"notB"
];
};
# Test how we handle white spaces in folder IDs
folders."foo bar" = {
path = "/var/lib/syncthing/foo-bar";
devices = [
"a"
];
ignorePatterns = [
"notB"
# Just test that an apostrophe doesn't break the curl config
# commands. See: https://github.com/NixOS/nixpkgs/issues/554744
"apostrophe'"
];
};
};
};
};
c = {
services.syncthing = {
enable = true;
openDefaultPorts = true;
cert = "${idC}/cert.pem";
key = "${idC}/key.pem";
settings = {
devices.a.id = lib.fileContents "${idA}/id";
devices.b.id = lib.fileContents "${idB}/id";
folders.bar = {
path = "/var/lib/syncthing/bar";
devices = [
"a"
"b"
];
type = "receiveencrypted";
};
folders.baz = {
path = "/var/lib/syncthing/baz";
devices = [
"a"
"b"
];
ignorePatterns = [
"notC"
];
};
};
};
testPassword = "it's a secret";
commonNodeConfigModule = {
services.syncthing = {
enable = true;
openDefaultPorts = true;
settings.devices = lib.mapAttrs (n: v: { inherit (v) id; }) nodeData;
};
};
nodeConfigModules = {
a = {
services.syncthing = {
inherit (nodeData.a) cert key;
guiAddress = "unix:///run/syncthing/syncthing.sock";
};
};
b = {
services.syncthing = { inherit (nodeData.b) cert key; };
};
c = {
services.syncthing = { inherit (nodeData.c) cert key; };
};
};
nodeFolderConfigModules = [
# "foo" is a folder that is synchronised only between nodes a and b.
rec {
a = {
services.syncthing.settings.folders.foo = {
path = "/var/lib/syncthing/foo";
devices = [
"a"
"b"
];
};
};
b = a;
c = { };
}
# "bar" is synchronised between a and c, and between b and c, but c only
# gets an encrypted copy, and a and b never synchronise directly to each
# other.
rec {
a =
{ config, ... }:
{
environment.etc.bar-encryption-password.text = testPassword;
services.syncthing.settings.folders.bar = {
path = "/var/lib/syncthing/bar";
devices = [
{
name = "c";
encryptionPasswordFile = "/etc/${config.environment.etc.bar-encryption-password.target}";
}
];
};
};
b = a;
c = {
services.syncthing.settings.folders.bar = {
path = "/var/lib/syncthing/bar";
devices = [
"a"
"b"
];
type = "receiveencrypted";
};
};
}
# "baz" is synchronised between all three nodes, but has filters on b and c
# that mean they shouldn't receive certain files.
{
a = {
services.syncthing.settings.folders.baz = {
path = "/var/lib/syncthing/baz";
devices = [
"b"
"c"
];
ignorePatterns = [ ];
};
};
b = {
services.syncthing.settings.folders.baz = {
path = "/var/lib/syncthing/baz";
devices = [
"a"
"c"
];
ignorePatterns = [
"notB"
# Just test that an apostrophe doesn't break the curl config
# commands. See: https://github.com/NixOS/nixpkgs/issues/554744
"apostrophe'"
];
};
};
c = {
services.syncthing.settings.folders.baz = {
path = "/var/lib/syncthing/baz";
devices = [
"a"
"b"
];
ignorePatterns = [ "notC" ];
};
};
}
# "foo bar" tests handling whitespace in folder IDs.
{
a = {
services.syncthing.settings.folders."foo bar" = {
path = "/var/lib/syncthing/foo-bar";
devices = [ "b" ];
};
};
b = {
services.syncthing.settings.folders."foo bar" = {
path = "/var/lib/syncthing/foo-bar";
devices = [ "a" ];
ignorePatterns = [ "notB" ];
};
};
c = { };
}
];
in
{
name = "syncthing-folders";
meta.maintainers = with pkgs.lib.maintainers; [ zarelit ];
# Run from the root of the nixpkgs repository with
#
# nix-build -A nixosTests.syncthing-folders.genNodeData &&
# ./result/bin/genNodeData.sh
#
# This generates new keys, certificates, and overall Syncthing config, and
# updates the certificate and key files and the ID file extracted from the
# overall Syncthing config file.
passthru.genNodeData = pkgs.writeShellApplication {
name = "genNodeData.sh";
runtimeInputs = with pkgs; [
syncthing
libxml2
];
text = ''
rm -r nixos/tests/syncthing/test-nodes
mkdir nixos/tests/syncthing/test-nodes
cd nixos/tests/syncthing/test-nodes
for d in ${lib.escapeShellArgs nodeNames}; do
mkdir -- "$d"
syncthing generate --home="$d"
xmllint --xpath 'string(configuration/device/@id)' "$d"/config.xml >"$d"/id
rm -f -- "$d"/.syncthing.tmp.* "$d"/config.xml
done
'';
};
nodes = lib.genAttrs nodeNames (n: {
imports = [
commonNodeConfigModule
nodeConfigModules."${n}"
]
++ map (builtins.getAttr n) nodeFolderConfigModules;
});
testScript = ''
start_all()

View File

@@ -1,6 +1,6 @@
{ lib, pkgs, ... }:
{
name = "syncthing";
name = "syncthing-no-settings";
meta.maintainers = with pkgs.lib.maintainers; [ chkno ];
nodes = {

View File

@@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBoDCCAVKgAwIBAgIJAJ7l/z0JF6aOMAUGAytlcDBKMRIwEAYDVQQKEwlTeW5j
dGhpbmcxIDAeBgNVBAsTF0F1dG9tYXRpY2FsbHkgR2VuZXJhdGVkMRIwEAYDVQQD
EwlzeW5jdGhpbmcwHhcNMjYwNDAyMDAwMDAwWhcNNDYwMzI4MDAwMDAwWjBKMRIw
EAYDVQQKEwlTeW5jdGhpbmcxIDAeBgNVBAsTF0F1dG9tYXRpY2FsbHkgR2VuZXJh
dGVkMRIwEAYDVQQDEwlzeW5jdGhpbmcwKjAFBgMrZXADIQBCsJ1O6QrxxFP/YKKj
WAjdZp07AiTIIC0/p/mHGbmyraNVMFMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQW
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
CXN5bmN0aGluZzAFBgMrZXADQQBqGFXpwvEoAc7N6mT9evXI3++STiTE6Lu3Z2z3
ecp5vU3fS5U+b0fO4BEUrNoaDdXurfOal6+LoydAnJcFamwN
-----END CERTIFICATE-----

View File

@@ -0,0 +1 @@
F2ACIUG-FML5RHY-ATV55ZN-5KGVUIV-RWFG3Y6-QPLTKZB-NWUZAGD-7QRCWAP

View File

@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIE2ls259KcQgtizG7hwP3aBhlYBNuPJwSBG8d4uVBFOh
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBnzCCAVGgAwIBAgIIKyLKAcqLWn4wBQYDK2VwMEoxEjAQBgNVBAoTCVN5bmN0
aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0ZWQxEjAQBgNVBAMT
CXN5bmN0aGluZzAeFw0yNjA0MDIwMDAwMDBaFw00NjAzMjgwMDAwMDBaMEoxEjAQ
BgNVBAoTCVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0
ZWQxEjAQBgNVBAMTCXN5bmN0aGluZzAqMAUGAytlcAMhABoahydRmwpbCII6mz9i
E8FeGH7YdHqgMeAmLiFZu2wMo1UwUzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYw
FAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwFAYDVR0RBA0wC4IJ
c3luY3RoaW5nMAUGAytlcANBADFVKB2vF16j0gc/h71x3vUi042FbmXTPk9kMb2O
9O0NnWSoMuOGPFDEoHWBFuUuixQ/3cw45zw+fea28m95gQo=
-----END CERTIFICATE-----

View File

@@ -0,0 +1 @@
LOB4D2H-OYG64QZ-NDX43LJ-NYR4HQU-BRNXCNI-ERACWFP-OVURLBI-63MR5QP

View File

@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIJtOML1Tshk03rB41IX5ajEeem50CdWzHDimotheTyZi
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBoDCCAVKgAwIBAgIJAIZk5+sv3EbTMAUGAytlcDBKMRIwEAYDVQQKEwlTeW5j
dGhpbmcxIDAeBgNVBAsTF0F1dG9tYXRpY2FsbHkgR2VuZXJhdGVkMRIwEAYDVQQD
EwlzeW5jdGhpbmcwHhcNMjYwNDAyMDAwMDAwWhcNNDYwMzI4MDAwMDAwWjBKMRIw
EAYDVQQKEwlTeW5jdGhpbmcxIDAeBgNVBAsTF0F1dG9tYXRpY2FsbHkgR2VuZXJh
dGVkMRIwEAYDVQQDEwlzeW5jdGhpbmcwKjAFBgMrZXADIQB0QK+PM7oLutgCKoIo
UOh8/XiSmGJWbkN175hALTIaYKNVMFMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQW
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
CXN5bmN0aGluZzAFBgMrZXADQQAbedm895vhgYizMXc38IwhquYv2S4ORHZac0Bw
ZYKJKze7EhKzqvdxcU5uVIUaMPSGi86wtmmsiijfhY8rnD0E
-----END CERTIFICATE-----

View File

@@ -0,0 +1 @@
MPGAF2L-AUIF5DE-UCI3AMX-PTGF3ZI-XDS6UJI-YUU4ZWT-UUWY7X6-N2DAAQG

View File

@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIMSmcIlXQTKkaMaOLh+zsgU1ULCvCz949E56OzQ1dsMK
-----END PRIVATE KEY-----

View File

@@ -126,6 +126,8 @@ in
networking.firewall.allowedTCPPorts = [ 80 ];
};
interactive.sshBackdoor.enable = true;
testScript =
let
changePassword = pkgs.writeText "change-password.py" ''

View File

@@ -5,9 +5,9 @@ let
in
{
sublime4 = common {
buildVersion = "4200";
x64sha256 = "NvacVRrRjuRgAr5NnFI/5UXZO2f+pnvupzHnJARLRp8=";
aarch64sha256 = "z0tqp06ioqqwLhRFmc+eSkI8u5VDwiH32hCVqVSVVmo=";
buildVersion = "4215";
x64sha256 = "wVP0GNOrJrkOzOjAKoLk6WECXtemX34lBgpUV9Q+iNk=";
aarch64sha256 = "0xRmWkJy8zVRUDQyNyo0UW27WpoS3OKkcELBlC9m7nk=";
} { };
sublime4-dev = common {

View File

@@ -7,8 +7,8 @@ vscode-utils.buildVscodeMarketplaceExtension {
mktplcRef = {
publisher = "ms-azuretools";
name = "vscode-containers";
version = "2.5.1";
hash = "sha256-FHS93HCKHFzleRIJP+pxpdTZZjqBkZOjHlmJ5M0ojbs=";
version = "2.5.2";
hash = "sha256-ERpwIOxLZxelWPRFYwsaEcbnOxW3cC3vGOKIkg92ztw=";
};
meta = {

View File

@@ -88,14 +88,14 @@ let
in
stdenv.mkDerivation rec {
pname = "qgis-unwrapped";
version = "4.2.1";
version = "4.2.2";
outputs = [ "out" ] ++ lib.optional (!stdenv.hostPlatform.isDarwin) "man";
src = fetchFromGitHub {
owner = "qgis";
repo = "QGIS";
rev = "final-${lib.replaceStrings [ "." ] [ "_" ] version}";
hash = "sha256-tdZNSA6kZHwS96YRbN7YF+ODPJrnINa/QGCo8pw196I=";
hash = "sha256-gEUShI09n7fpLcfKaNmiatB8pco0yJ227Ge7pPnMxCY=";
};
postPatch = ''

View File

@@ -292,13 +292,13 @@
"vendorHash": "sha256-3o6YRDrq4rQhNAFyqiGJrAoxuAykWw85OExRGSE3kGI="
},
"datadog_datadog": {
"hash": "sha256-a84guU5oeG+BwmPoPCyGZRBOB/dzaPCLwxKH3BYSj6A=",
"hash": "sha256-Unz4DuY30UuEktduVQNjTfqwSpIXI4n3nJlHE6piiOw=",
"homepage": "https://registry.terraform.io/providers/DataDog/datadog",
"owner": "DataDog",
"repo": "terraform-provider-datadog",
"rev": "v4.21.0",
"rev": "v4.22.0",
"spdx": "MPL-2.0",
"vendorHash": "sha256-KBzkbWlFM0SB+YzVU4YIaJxvQlMHalPeKPvvHh4L2kw="
"vendorHash": "sha256-77BV9XKSwB0s2Grfh3w0XESxYvohNPG6g22+5cVUiVU="
},
"datadrivers_nexus": {
"hash": "sha256-+MTyr7voagijpqTb7vswO8PAFZpxz3UWAQLEs0os4+s=",
@@ -715,11 +715,11 @@
"vendorHash": "sha256-47xWjlzpQ/EYzjbuuMKQiu5cfYAXdYkXRl+AOEP+sA4="
},
"heroku_heroku": {
"hash": "sha256-AmHgAlz4J3l9OCjUMVxLMWtKZB1LpNOyX1exUI6fWHA=",
"hash": "sha256-FnJn/kFnTOOcnJtVdlSFpZh9S+BZodhk6IcvG0DFaLg=",
"homepage": "https://registry.terraform.io/providers/heroku/heroku",
"owner": "heroku",
"repo": "terraform-provider-heroku",
"rev": "v5.4.0",
"rev": "v5.4.1",
"spdx": "MPL-2.0",
"vendorHash": null
},
@@ -1391,13 +1391,13 @@
"vendorHash": "sha256-Bat/S4e5vzT0/XOhJ9zCWLa4IE4owLC6ec1yvEh+c0Y="
},
"topicusonderwijs_octodns": {
"hash": "sha256-gbw0Na3m5X5CjoaXHPREfQIpwzQ9hpa7A3Hn+rwcjEA=",
"hash": "sha256-ewCWuQlmyrP0mrIz0k+YYUzheeFBPh1bEyRueFC8b3w=",
"homepage": "https://registry.terraform.io/providers/topicusonderwijs/octodns",
"owner": "topicusonderwijs",
"repo": "terraform-provider-octodns",
"rev": "v1.2.0",
"rev": "v1.3.0",
"spdx": "MPL-2.0",
"vendorHash": "sha256-da0+/aLNEuMZWD7+zMUGpc1Ch5VKyN+EyO0Mp4mZWv8="
"vendorHash": "sha256-YDTR4twyHhBqOEPbzMtkO2DWj41VhJ1PAVU3nNVh3l8="
},
"trozz_pocketid": {
"hash": "sha256-+jIdj9tUV0ScX4y7lm3IWSLHsxwHGp+KXVfaY0K86uk=",

View File

@@ -0,0 +1,50 @@
{
lib,
python3Packages,
fetchFromGitHub,
callPackage,
}:
python3Packages.buildPythonApplication {
pname = "aclpubcheck";
version = "0.1-unstable-2026-09-11";
pyproject = true;
src = fetchFromGitHub {
owner = "acl-org";
repo = "aclpubcheck";
rev = "237bee3a554f2d2fcda69cd0cf1edf4168e3d339"; # No Git Tags
hash = "sha256-s9kegTZOZEgGx0Yj8jOfzAyjyy1EuabOybMDBoodRvo=";
};
strictDeps = true;
__structuredAttrs = true;
build-system = with python3Packages; [
setuptools
];
dependencies = with python3Packages; [
tqdm
termcolor
pandas
pdfplumber
rebiber
pybtex
pylatexenc
unidecode
tsv
];
passthru.tests = {
example-pdf = callPackage ./test { };
};
meta = {
description = "Tool for checking ACL paper submissions";
homepage = "https://github.com/acl-org/aclpubcheck";
license = with lib.licenses; [ mit ];
mainProgram = "aclpubcheck";
maintainers = with lib.maintainers; [ Luflosi ];
};
}

View File

@@ -0,0 +1,29 @@
{
runCommand,
aclpubcheck,
}:
runCommand "aclpubcheck-test-example-pdf" { nativeBuildInputs = [ aclpubcheck ]; } ''
# aclpubcheck prints out the path that was passed to it, which may change over time if it is a Nix store path.
# Since we're comparing the output of aclpubcheck, this would break the test.
# To avoid this, pass aclpubcheck a relative path to a symlink in the current working directory instead of the absolute path.
# Simply using `cd` to change directories into the example directory does not work,
# since aclpubcheck wants to write some files to the current working directory.
ln -s '${aclpubcheck.src}/example/2023.acl-tutorials.1.pdf' 2023.acl-tutorials.1.pdf
aclpubcheck --paper_type long 2023.acl-tutorials.1.pdf > actual-output.txt
exit_code="$?"
if [ "$exit_code" != 0 ]; then
echo "Exit code of aclpubcheck was $exit_code while 0 was expected."
exit 1
fi
if ! diff '${./expected-output.txt}' actual-output.txt; then
echo
echo "ERROR: The output was different than expected!"
echo "The diff is above."
exit 1
fi
touch "$out"
''

View File

@@ -0,0 +1,28 @@
Checking 2023.acl-tutorials.1.pdf
Found text violation: Margin.BOTTOM {'top': 780, 'bottom': 841}
Found text violation: Margin.BOTTOM {'top': 780, 'bottom': 841}
Found text violation: Margin.BOTTOM {'top': 780, 'bottom': 841}
Found text violation: Margin.BOTTOM {'top': 780, 'bottom': 841}
Found text violation: Margin.BOTTOM {'top': 780, 'bottom': 841}
Found text violation: Margin.BOTTOM {'top': 780, 'bottom': 841}
Found text violation: Margin.BOTTOM {'top': 780, 'bottom': 841}
Found text violation: Margin.BOTTOM {'top': 780, 'bottom': 841}
Found text violation: Margin.BOTTOM {'top': 780, 'bottom': 841}
Found text violation: Margin.BOTTOM {'top': 780, 'bottom': 841}
Errors. Check errors-2023.acl-tutorials.1.json for details.
Error (Margin): Text on page 1 bleeds into the bottom margin. It should be empty (e.g., without page number) and populated when building the proceedings.
Error (Margin): Text on page 2 bleeds into the bottom margin. It should be empty (e.g., without page number) and populated when building the proceedings.
Error (Margin): Text on page 3 bleeds into the bottom margin. It should be empty (e.g., without page number) and populated when building the proceedings.
Error (Margin): Text on page 4 bleeds into the bottom margin. It should be empty (e.g., without page number) and populated when building the proceedings.
Error (Margin): Text on page 5 bleeds into the bottom margin. It should be empty (e.g., without page number) and populated when building the proceedings.
Error (Margin): Text on page 6 bleeds into the bottom margin. It should be empty (e.g., without page number) and populated when building the proceedings.
Error (Margin): Text on page 7 bleeds into the bottom margin. It should be empty (e.g., without page number) and populated when building the proceedings.
Error (Margin): Text on page 8 bleeds into the bottom margin. It should be empty (e.g., without page number) and populated when building the proceedings.
Error (Margin): Text on page 9 bleeds into the bottom margin. It should be empty (e.g., without page number) and populated when building the proceedings.
Error (Margin): Text on page 10 bleeds into the bottom margin. It should be empty (e.g., without page number) and populated when building the proceedings.
We detected 10 errors and 0 warnings in your paper.
In general, it is required that you fix errors for your paper to be published. Fixing warnings is optional, but recommended.
Important: Some of the margin errors may be spurious. The library detects the location of images, but not whether they have a white background that blends in.
Important: Some of the warnings generated for citations may be spurious and inaccurate, due to parsing and indexing errors.
We encourage you to double check the citations and update them depending on the latest source. If you believe that your citation is updated and correct, then please ignore those warnings.

View File

@@ -10,16 +10,16 @@
let
sources = {
x86_64-linux = {
url = "https://dl.google.com/agy-extensions/releases/linux/agy-acp-server-agy_acp_server_1.1.1-linux-x86_64.zip";
hash = "sha256-OPYtAbMt6wkHs9OacewwH9Njafb/0c8mLUrzhRd/ed8=";
url = "https://dl.google.com/agy-extensions/releases/linux/agy-acp-server-1.2.1-linux-x86_64.zip";
hash = "sha256-n78L1YSiZHgWH2N8q9dRE/clQchC0Uj1eO8aap7cuEM=";
};
aarch64-linux = {
url = "https://dl.google.com/agy-extensions/releases/linux/agy-acp-server-agy_acp_server_1.1.1-linux-arm64.zip";
hash = "sha256-7WnmSzCPyxI6tUvzJ3v5yw1lEGT4hepaqw/1IMcXU5g=";
url = "https://dl.google.com/agy-extensions/releases/linux/agy-acp-server-1.2.1-linux-arm64.zip";
hash = "sha256-fn70CIvBheGvQgQCng9OxCEK8gck8/8mIYasC86mqg4=";
};
aarch64-darwin = {
url = "https://dl.google.com/agy-extensions/releases/macos/agy-acp-server-agy_acp_server_1.1.1-darwin-arm64.zip";
hash = "sha256-/fqRVlLNt7qAhcyP/+0HLL4AklGqLJUaq92geowooYk=";
url = "https://dl.google.com/agy-extensions/releases/macos/agy-acp-server-1.2.1-darwin-arm64.zip";
hash = "sha256-D6uZOIEuazKztUPmXk86ACXO73VUE9sTVC2am4HqgDw=";
};
};
@@ -29,7 +29,7 @@ let
in
stdenv.mkDerivation {
pname = "antigravity-acp";
version = "1.1.1"; # https://github.com/agentclientprotocol/registry/blob/main/antigravity-acp/agent.json
version = "1.2.1"; # https://github.com/agentclientprotocol/registry/blob/main/antigravity-acp/agent.json
src = fetchurl {
inherit (srcInfo) url hash;

View File

@@ -23,11 +23,11 @@ stdenvNoCC.mkDerivation (
sources = {
aarch64-darwin = {
name = "Aptakube_${finalAttrs.version}_universal.dmg";
hash = "sha256-wDfb2B5KMIkQcwO9JkX2b5FpgzJaUSKZTFQCqRkfLls=";
hash = "sha256-qIaTUvZ1RLCucB1FG92rh8rApltstxaq8XEXblnFKrI=";
};
x86_64-linux = {
name = "aptakube_${finalAttrs.version}_amd64.deb";
hash = "sha256-HDbRWTNFX2V0kgeFIZPLNLRTRxx/eETQiyLD1Yf6YYg=";
hash = "sha256-YDPEy3wiPohl0Ql5ITNA2UxdpuG4tjGnA0WN6Qt9pd8=";
};
};
@@ -42,7 +42,7 @@ stdenvNoCC.mkDerivation (
in
{
pname = "aptakube";
version = "1.20.4";
version = "1.20.5";
__structuredAttrs = true;
strictDeps = true;

View File

@@ -12,13 +12,13 @@
}:
python313Packages.buildPythonApplication (finalAttrs: {
pname = "bazarr";
version = "1.6.1";
version = "1.6.2";
src = fetchFromGitHub {
owner = "morpheus65535";
repo = "bazarr";
tag = "v${finalAttrs.version}";
hash = "sha256-m9429gSt9xrA3N9w6eIBtHmQWOZDiRKIsu52fusQutU=";
hash = "sha256-5bhNbLfuL1wzraO3UypRRstC1+ULTaFVNJC++Qov6AE=";
};
dependencies = with python313Packages; [
@@ -76,7 +76,7 @@ python313Packages.buildPythonApplication (finalAttrs: {
nodejs = nodejs_24;
npmDepsHash = "sha256-82hLGQBuymU7DhDn+aYQIay1cVR+d4E3nU+ZNhJ8xJ0=";
npmDepsHash = "sha256-uvUXk5+/WOfFRuBnC/SQOkau+0uIkJ4OTofMXckmwzw=";
nativeBuildInputs = [ dart-sass ];

View File

@@ -11,7 +11,7 @@
stdenv.mkDerivation rec {
pname = "blackfire";
version = "2026.9.0";
version = "2026.9.1";
src =
passthru.sources.${stdenv.hostPlatform.system}
@@ -60,19 +60,19 @@ stdenv.mkDerivation rec {
sources = {
"x86_64-linux" = fetchurl {
url = "https://packages.blackfire.io/debian/pool/any/main/b/blackfire/blackfire_${version}_amd64.deb";
hash = "sha256-mm93TmfrSMP5+hVtWQ2CkUqmDqYraL4H7NVLXZ7xDqs=";
hash = "sha256-ElktV5SSt4zhvVnQS8qljbPDGH0qM85i7WztyoDyvcM=";
};
"i686-linux" = fetchurl {
url = "https://packages.blackfire.io/debian/pool/any/main/b/blackfire/blackfire_${version}_i386.deb";
hash = "sha256-nz0YYTdH0Rcs4f0aJu8Bkg/NwLNxiFwSq8kkRoa+VEA=";
hash = "sha256-vl6PvMsqc3GyIsrYl1WpV1psxuuszSfN1TdRH5FW9qE=";
};
"aarch64-linux" = fetchurl {
url = "https://packages.blackfire.io/debian/pool/any/main/b/blackfire/blackfire_${version}_arm64.deb";
hash = "sha256-RTTNU3c9gJQRh8vjrCnhPh/mKWKs9jHUd3gund3UZWM=";
hash = "sha256-fjcp+gOHna7grTl972jslY8hYdjWhfxbA4ncHfCAkGw=";
};
"aarch64-darwin" = fetchurl {
url = "https://packages.blackfire.io/blackfire/${version}/blackfire-darwin_arm64.pkg.tar.gz";
hash = "sha256-xa9lqDOVS0uPLeAyQ3fvkp3SNN8Hhv66gitjgKk/p6M=";
hash = "sha256-xNn78U4jdABzWrSKMSSZXE5tuf/SRK8OwdhldKBBKk0=";
};
};

View File

@@ -16,47 +16,47 @@ let
phpMajor = lib.versions.majorMinor php.version;
inherit (stdenv.hostPlatform) system;
version = "2026.9.0";
version = "2026.9.2";
hashes = {
"x86_64-linux" = {
system = "amd64";
hash = {
"8.1" = "sha256-0AAgiBdiIQOxSSttD2ERzSTqPM1rLwlQFHZ6ARRSgmI=";
"8.2" = "sha256-usSNoM1XeVWKuHLlSMvONAucVa1ZEAb3+I66oOnzGB0=";
"8.3" = "sha256-65GoEhgFsQbQleSVuRnHPSZAiCfEUmyVWWhnybnrgaA=";
"8.4" = "sha256-0if1fQa7b41TjC3d1ck65cJP7lKdoL670V9t+PLL+qk=";
"8.5" = "sha256-3k5jQ+vbxLGp78MRzjiw7uP+tCcEs5gAYM2MEoiYdtk=";
"8.1" = "sha256-Rh26CehFWvZbri+wE+NOit6Mc6LB55IVZ0FT63/GBew=";
"8.2" = "sha256-HoaC8XAGxqPvQPkpsqTjBxd6Z8FnB/cjk5uC9ogNMAs=";
"8.3" = "sha256-nRwvQYootheK0qEWbEJoC82butcJRFz65zhxey56/Bk=";
"8.4" = "sha256-rqo9U0S2Cuhy+CiILeXo2DW22y9xb/7QB3l7Et0IbTM=";
"8.5" = "sha256-hf/pe+Go8qhHxqAodbLyn60t1FJGZtQ965pMFCJ4t0M=";
};
};
"i686-linux" = {
system = "i386";
hash = {
"8.1" = "sha256-fmmbY4ecnE05XNxGKq11HcYhs9z7SggLx9iXICHE6DQ=";
"8.2" = "sha256-9WP+FpULl0PQJ+0qxxZfm5xJS/A6N137yGk9gv4cYvI=";
"8.3" = "sha256-Io2gGAhXLAVdHoaKwKvJWA1N71IJAKeJkudLs8DZUl8=";
"8.4" = "sha256-JoGiB8ew3D/qSi7Pg/q67mXLsUy4UDVsazgxgb5BJTM=";
"8.5" = "sha256-bSfbhFHV8Zs4cpOfDnKItNlF9++0opUtosTpnosacdU=";
"8.1" = "sha256-BgOAsLqMqsyXfEIgx/Buaz4jjU1TN8lxVPTSvQzCn7M=";
"8.2" = "sha256-D1FiwfYF8tRM1uMEWPauY+SzQNyL9kszVb5pD5vOBwA=";
"8.3" = "sha256-SZ9KFC+ISp5LgElLeQTOInlgoSqWV9+oRdC6yJ5P3WA=";
"8.4" = "sha256-x4ZTJs1VLipdEEAhjb+pG4Q25a2czBkEPtucHGdZINw=";
"8.5" = "sha256-zzSZsGu2POr97O5fwKRCwJqs6+lO1HqfG6o5HKL4zVQ=";
};
};
"aarch64-linux" = {
system = "arm64";
hash = {
"8.1" = "sha256-7/2Q9Kx3ZEpI0Inj88CfTDzr0sjVpws3DH8KTP26PR0=";
"8.2" = "sha256-suFGyE94wY4xHfPk77OXzkqU+Ulb+f42drDZY/M9ZNs=";
"8.3" = "sha256-z5IfXX7DElerdRTnq3R95t8IvG0Hl9EKXBw1QbRlDkY=";
"8.4" = "sha256-ceTjQ6gtiWJEte5WuVuyc+eTEb3khobYoCWNGP+Vkeo=";
"8.5" = "sha256-1jX564B/tLBgb7jN6MB5DfpRgYy0xxmtAaAv768FQqo=";
"8.1" = "sha256-QGe/XJt2N7UFpW0ahKo+hCZO7X80L31hAp0D6oreGt4=";
"8.2" = "sha256-QyhbXXlhBdoNUYJcPOt4w4sA45ELtY4IERD8GUS3I4c=";
"8.3" = "sha256-+l9RGCmhQsdtqntfUk22d1zVIcAxuLw4mHpe4WcfGr8=";
"8.4" = "sha256-hUI/z+PNAj7gl9UCGes/TnhV6zK82LMfhEfi72gsy7c=";
"8.5" = "sha256-MaqGbXacLeDUPlskuETtzv1cXZTO0/EF0IW49N6eZMY=";
};
};
"aarch64-darwin" = {
system = "arm64";
hash = {
"8.1" = "sha256-JQZKX8qChvBS3S8cqtxmooZMsFlFqfffnQbNldYNR+M=";
"8.2" = "sha256-nGQdweskEw9tiK51IGcu7cGKJJwtmNBL9fZLMUCuiB8=";
"8.3" = "sha256-sHQOg6QC+Mm5KwQVwKmeOVR3fUkN2NH9utHs667Oipw=";
"8.4" = "sha256-Btrz+U9ejW/Jl1cWBRt5XGV1IzjxK+FJaQwXIgYR/NI=";
"8.5" = "sha256-zSQeBioU/3c7HrqEz+9z8vam3EHkR0KbC80/mIuTAFE=";
"8.1" = "sha256-oG7Doie9hoieBM649S1XlagBaYAWAjJu2PrzYSDLKL0=";
"8.2" = "sha256-MJi0cve8+eItBcC6UkuxYTzclB3OMC+Hhlmd++xD1MU=";
"8.3" = "sha256-3oJtMuVKGUgpduMp7snSdGE/BH764EA7yz+N70+qFNg=";
"8.4" = "sha256-0HOCBB9dgU9Vq5/F0iKCzumjwT81qxHElPsLGKgVhr0=";
"8.5" = "sha256-Hi9bC/CigkA3VWFTqfE7JzBcGGJAhUwnmMndHgbFIW4=";
};
};
};

View File

@@ -395,10 +395,7 @@ stdenv.mkDerivation {
inherit (nixosTests)
ceph-multi-node-bluestore
ceph-multi-node-bluestore-cephfs
ceph-multi-node-deprecated-filestore
ceph-single-node-bluestore
ceph-single-node-bluestore-dmcrypt
ceph-single-node-deprecated-filestore
;
};
};

View File

@@ -6,11 +6,11 @@
applyPatches (final: {
pname = "ceph-src";
version = "20.2.3";
version = "20.2.4";
src = fetchurl {
url = "https://download.ceph.com/tarballs/ceph-${final.version}.tar.gz";
hash = "sha256-y3bZm2lkHiebXYNbZA7jN4VXCLaDEElYvpyuglLISi0=";
hash = "sha256-XzRWkkGiiQRGuTHwbNhE+TvKZl90CiBjFCnS1Vsemzc=";
};
patches = [

View File

@@ -75,6 +75,8 @@ appimageTools.wrapType2 rec {
install -Dm444 ${contents}/usr/share/icons/hicolor/48x48/apps/app.png $out/share/icons/hicolor/48x48/apps/cisco-packet-tracer-9.png
cp -r ${contents}/usr/share/icons/gnome/48x48/mimetypes $out/share/icons/hicolor/48x48/
cp -r ${contents}/usr/share/mime $out/share/
for desktop in $out/share/applications/*.desktop; do
sed -i '/^\[Desktop Entry\]/a StartupWMClass=PacketTracer' "$desktop"
done

View File

@@ -0,0 +1,161 @@
{
lib,
stdenv,
fetchFromGitLab,
pkg-config,
gtk4,
vala,
enchant,
wrapGAppsHook3,
meson,
ninja,
desktop-file-utils,
gnome-online-accounts,
gsettings-desktop-schemas,
adwaita-icon-theme,
libpeas2,
libsecret,
gmime3,
isocodes,
icu,
libxml2,
gettext,
sqlite,
json-glib,
itstool,
libgee,
webkitgtk_6_0,
python3,
gnutls,
cacert,
xvfb-run,
glibcLocales,
dbus,
shared-mime-info,
libunwind,
folks,
glib-networking,
gobject-introspection,
gspell,
libstemmer,
libytnef,
libhandy,
gsound,
cmake,
gcr_4,
libspelling,
libadwaita,
gst_all_1,
}:
stdenv.mkDerivation (finalAttrs: {
pname = "convey";
version = "50.2-1";
src = fetchFromGitLab {
domain = "gitlab.gnome.org";
owner = "donnybeelo";
repo = "convey";
tag = finalAttrs.version;
hash = "sha256-YFdAhC7xPGaqEdDuv1Kb6b1NHjivfkc+TnXEGhpkdQw=";
};
strictDeps = true;
__structuredAttrs = true;
nativeBuildInputs = [
desktop-file-utils
gettext
gobject-introspection
itstool
libxml2 # for xmllint for xml-stripblanks preprocessing
meson
ninja
pkg-config
python3
vala
wrapGAppsHook3
cmake
];
buildInputs = [
adwaita-icon-theme
enchant
folks
gcr_4
glib-networking
gmime3
gnome-online-accounts
gsettings-desktop-schemas
gsound
gspell
gst_all_1.gst-plugins-bad
gst_all_1.gst-plugins-base
gtk4
icu
isocodes
json-glib
libadwaita
libgee
libhandy
libpeas2
libsecret
libspelling
libstemmer
libunwind
libxml2
libytnef
sqlite
webkitgtk_6_0
];
nativeCheckInputs = [
dbus
gnutls # for certtool
cacert # trust store for glib-networking
xvfb-run
glibcLocales # required by Geary.ImapDb.DatabaseTest/utf8_case_insensitive_collation
];
mesonFlags = [
"-Dprofile=release"
"-Dcontractor=enabled" # install the contractor file (Pantheon specific)
];
postPatch = ''
chmod +x build-aux/git_version.py
patchShebangs build-aux/git_version.py
chmod +x desktop/convey-attach
'';
# Some tests time out.
doCheck = false;
checkPhase = ''
runHook preCheck
NO_AT_BRIDGE=1 \
GIO_EXTRA_MODULES=$GIO_EXTRA_MODULES:${glib-networking}/lib/gio/modules \
HOME=$TMPDIR \
XDG_DATA_DIRS=$XDG_DATA_DIRS:${gsettings-desktop-schemas}/share/gsettings-schemas/${gsettings-desktop-schemas.name}:${shared-mime-info}/share:${folks}/share/gsettings-schemas/${folks.name} \
xvfb-run -s '-screen 0 800x600x24' dbus-run-session \
--config-file=${dbus}/share/dbus-1/session.conf \
meson test -v --no-stdsplit
runHook postCheck
'';
preFixup = ''
# Add geary to path for geary-attach
gappsWrapperArgs+=(--prefix PATH : "$out/bin")
'';
meta = {
homepage = "https://gitlab.gnome.org/donnybeelo/convey";
changelog = "https://gitlab.gnome.org/donnybeelo/convey/-/blob/${finalAttrs.version}/NEWS?ref_type=tags";
description = "Mail client for GNOME 3";
teams = [ lib.teams.gnome ];
license = lib.licenses.lgpl21Plus;
platforms = lib.platforms.linux;
};
})

View File

@@ -7,14 +7,14 @@
dokuwiki,
}:
stdenv.mkDerivation rec {
stdenv.mkDerivation (finalAttrs: {
pname = "dokuwiki";
version = "2026-07-14c";
src = fetchFromGitHub {
owner = "dokuwiki";
repo = "dokuwiki";
rev = "release-${version}";
rev = "release-${finalAttrs.version}";
sha256 = "sha256-84kMuFTWYo6Cjd6qpkZsLZoECIP9IzSrc9dX1uKMp0M=";
};
@@ -49,9 +49,9 @@ stdenv.mkDerivation rec {
mkdir -p $out/share/dokuwiki
cp -r * $out/share/dokuwiki
cp ${preload} $out/share/dokuwiki/inc/preload.php
cp ${phpLocalConfig} $out/share/dokuwiki/conf/local.php
cp ${phpPluginsLocalConfig} $out/share/dokuwiki/conf/plugins.local.php
cp ${finalAttrs.preload} $out/share/dokuwiki/inc/preload.php
cp ${finalAttrs.phpLocalConfig} $out/share/dokuwiki/conf/local.php
cp ${finalAttrs.phpPluginsLocalConfig} $out/share/dokuwiki/conf/plugins.local.php
runHook postInstall
'';
@@ -110,9 +110,10 @@ stdenv.mkDerivation rec {
license = lib.licenses.gpl2Only;
homepage = "https://www.dokuwiki.org";
platforms = lib.platforms.all;
identifiers.cpeParts = lib.meta.cpeFullVersionWithVendor "dokuwiki" finalAttrs.version;
maintainers = with lib.maintainers; [
_1000101
e1mo
];
};
}
})

View File

@@ -17,8 +17,8 @@ stdenv.mkDerivation (finalAttrs: {
src = fetchFromGitHub {
owner = "dosfstools";
repo = "dosfstools";
rev = "v${finalAttrs.version}";
sha256 = "sha256-2gxB0lQixiHOHw8uTetHekaM57fvUd9zOzSxWnvUz/c=";
tag = "v${finalAttrs.version}";
hash = "sha256-2gxB0lQixiHOHw8uTetHekaM57fvUd9zOzSxWnvUz/c=";
};
patches = [
@@ -64,6 +64,7 @@ stdenv.mkDerivation (finalAttrs: {
description = "Utilities for creating and checking FAT and VFAT file systems";
homepage = "https://github.com/dosfstools/dosfstools";
platforms = lib.platforms.unix;
license = lib.licenses.gpl3;
license = lib.licenses.gpl3Plus;
maintainers = [ lib.maintainers.quantenzitrone ];
};
})

View File

@@ -30,13 +30,13 @@ let
in
stdenv.mkDerivation (finalAttrs: {
pname = "element-desktop";
version = "1.12.28";
version = "1.12.29";
src = fetchFromGitHub {
owner = "element-hq";
repo = "element-web";
tag = "v${finalAttrs.version}";
hash = "sha256-goP/f1Go7227R2euXu8aJrwHeUp84DQ+18ztyf4uXhM=";
hash = "sha256-0LiasFrVMnMX1Z4TcP+Eti58X7+ICksqKnerQKj2ayI=";
};
pnpmDeps = fetchPnpmDeps {
@@ -155,7 +155,7 @@ stdenv.mkDerivation (finalAttrs: {
'';
# The desktop item properties should be kept in sync with data from upstream:
# https://github.com/element-hq/element-desktop/blob/develop/package.json
# https://github.com/element-hq/element-web/blob/develop/apps/desktop/package.json
desktopItems = [
(makeDesktopItem {
name = "element-desktop";

View File

@@ -25,13 +25,13 @@ let
in
stdenv.mkDerivation (finalAttrs: {
pname = "element-web";
version = "1.12.28";
version = "1.12.29";
src = fetchFromGitHub {
owner = "element-hq";
repo = "element-web";
tag = "v${finalAttrs.version}";
hash = "sha256-goP/f1Go7227R2euXu8aJrwHeUp84DQ+18ztyf4uXhM=";
hash = "sha256-0LiasFrVMnMX1Z4TcP+Eti58X7+ICksqKnerQKj2ayI=";
};
pnpmDeps = fetchPnpmDeps {

View File

@@ -0,0 +1,40 @@
{
rustPlatform,
fetchFromGitHub,
libcosmicAppHook,
lib,
}:
rustPlatform.buildRustPackage (finalAttrs: {
pname = "enroll";
version = "1.2.8";
__structuredAttrs = true;
src = fetchFromGitHub {
owner = "cosmic-utils";
repo = "enroll";
tag = "v${finalAttrs.version}";
hash = "sha256-mzB1BCurNoY0JB4Tx+yR6whzBCplVmQqGKC2vmJbjYI=";
};
cargoHash = "sha256-WfzSdvU8HoSLrZ3l9n4J/qFaPvcBHYcNlcb7UC080ZE=";
nativeBuildInputs = [ libcosmicAppHook ];
# The justfile installs under a mismatched appid case; match the desktop file's Icon= instead.
postInstall = ''
install -Dm0644 resources/org.cosmic_utils.enroll.desktop -t $out/share/applications
install -Dm0644 resources/org.cosmic_utils.enroll.metainfo.xml -t $out/share/metainfo
install -Dm0644 resources/icons/hicolor/scalable/apps/enroll.svg \
$out/share/icons/hicolor/scalable/apps/org.cosmic_utils.enroll.svg
'';
meta = {
description = "Fingerprint enrollment for COSMIC";
license = lib.licenses.mpl20;
maintainers = with lib.maintainers; [ marcusramberg ];
homepage = "https://github.com/cosmic-utils/enroll";
changelog = "https://github.com/cosmic-utils/enroll/releases/tag/v${finalAttrs.version}";
mainProgram = "cosmic-utils-enroll";
platforms = lib.platforms.linux;
};
})

View File

@@ -9,7 +9,6 @@
libadwaita,
librsvg,
gettext,
itstool,
libxml2,
meson,
ninja,
@@ -20,11 +19,11 @@
stdenv.mkDerivation (finalAttrs: {
pname = "gnome-mahjongg";
version = "49.1.1";
version = "51.0";
src = fetchurl {
url = "mirror://gnome/sources/gnome-mahjongg/${lib.versions.major finalAttrs.version}/gnome-mahjongg-${finalAttrs.version}.tar.xz";
hash = "sha256-6e3TGsJpi42aW+HRHGDUNFCoifh2nMoL7zOVoRpdX9E=";
hash = "sha256-g4moJK97Xq6S1snGLqn94VJ/iAwQ/lEkbTi+7DG4wog=";
};
nativeBuildInputs = [
@@ -34,7 +33,6 @@ stdenv.mkDerivation (finalAttrs: {
desktop-file-utils
pkg-config
libxml2
itstool
gettext
wrapGAppsHook4
glib # for glib-compile-schemas
@@ -47,6 +45,8 @@ stdenv.mkDerivation (finalAttrs: {
librsvg
];
doCheck = true;
passthru = {
updateScript = gnome.updateScript {
packageName = "gnome-mahjongg";
@@ -59,7 +59,7 @@ stdenv.mkDerivation (finalAttrs: {
description = "Disassemble a pile of tiles by removing matching pairs";
mainProgram = "gnome-mahjongg";
teams = [ lib.teams.gnome ];
license = lib.licenses.gpl2Plus;
license = lib.licenses.gpl3Plus;
platforms = lib.platforms.unix;
};
})

View File

@@ -6,16 +6,16 @@
buildGoModule (finalAttrs: {
pname = "go-judge";
version = "1.12.3";
version = "1.13.0";
src = fetchFromGitHub {
owner = "criyle";
repo = "go-judge";
rev = "v${finalAttrs.version}";
hash = "sha256-uRwB6Ir1A+RmSqeOp6rCdFnJgRqrrbatRFgKHzFtF9o=";
hash = "sha256-uxFpW4c1xISbgLUR1wDDoR0/+wUT326e69nTDGqCdOQ=";
};
vendorHash = "sha256-jbV58oJX9Bddq5aqi9rfpkrPdGmlyMM6CKrQf1C9ZgI=";
vendorHash = "sha256-qYB3IutGOKHiTt8kwqexgSyut6xbigdYw6A1I1pyG44=";
tags = [
"nomsgpack"

View File

@@ -8,16 +8,16 @@
buildGoModule (finalAttrs: {
pname = "goshs";
version = "2.1.6";
version = "2.1.7";
src = fetchFromGitHub {
owner = "goshs-labs";
repo = "goshs";
tag = "v${finalAttrs.version}";
hash = "sha256-0d4iB6Mtann0OZd/KyWnwq7+fCcWEibAzHSYe30Mce0=";
hash = "sha256-RnpzlAH5wbes40FkCvDhzwbg6oaHbMkg2I/U7Lm9IFs=";
};
vendorHash = "sha256-E+GZn7Trnz3KqzTsEfavWxP1dhsGPx4PyYYae8wjCb4=";
vendorHash = "sha256-G8QG2h44d8IjhfDGTt8ObTXOzv9jnz8HHB/Ctr4wU8c=";
patches = [
# No upstream fix yet; remove when updating to a release that uses goldmark 1.7.17 or later.

View File

@@ -1,21 +1,21 @@
{
"version": "0.0.299",
"version": "0.0.302.6",
"assets": {
"x86_64-linux": {
"url": "https://github.com/justrach/codegraff/releases/download/v0.0.299/graff-x86_64-linux.tar.gz",
"hash": "sha256-qVS6Q3ccVgaJMaCnRX44JRZulQciS02R+D3+rmt73wI="
"url": "https://github.com/justrach/codegraff/releases/download/v0.0.302.6/graff-x86_64-linux.tar.gz",
"hash": "sha256-d7J21dWTRlkdca4JCqoQFXPz/TVXBuo07JE7IsFErvg="
},
"aarch64-linux": {
"url": "https://github.com/justrach/codegraff/releases/download/v0.0.299/graff-aarch64-linux.tar.gz",
"hash": "sha256-GHGgCopNQMNKiRv/LElYbRVYif8L9TLUoUsku0uEPfU="
"url": "https://github.com/justrach/codegraff/releases/download/v0.0.302.6/graff-aarch64-linux.tar.gz",
"hash": "sha256-aJAs7dmk5B6OAJQagJrgfaJ1yO3q5pVms630OBtcehQ="
},
"aarch64-darwin": {
"url": "https://github.com/justrach/codegraff/releases/download/v0.0.299/graff-aarch64-macos.tar.gz",
"hash": "sha256-gyGFMTS8wH9cFGRBnCJtq0GtvxCg4VzNoVTnET+ELQ4="
"url": "https://github.com/justrach/codegraff/releases/download/v0.0.302.6/graff-aarch64-macos.tar.gz",
"hash": "sha256-6D4EsH0whHy3F1fhpyQ9eXVJ54HabS0zU1fJaN7YH2k="
},
"x86_64-darwin": {
"url": "https://github.com/justrach/codegraff/releases/download/v0.0.299/graff-x86_64-macos.tar.gz",
"hash": "sha256-akWs/kk/iptGnzDzzerdQbZoFF9k/HcguQvpvdnoSPs="
"url": "https://github.com/justrach/codegraff/releases/download/v0.0.302.6/graff-x86_64-macos.tar.gz",
"hash": "sha256-IcsCVeQt1zCYPpXN7lpUXMOeEmL8lTt3SAt5oKf8pCw="
}
}
}

View File

@@ -17,9 +17,15 @@ python3Packages.buildPythonApplication (finalAttrs: {
sha256 = "sha256-dOHFLw8suvpuZkcKEzq5HktMYBGE7+vtTD609TkAFfw=";
};
# python3.8+ changed AST parsing, so until upstream builds against newer versions this has to do
postPatch = ''
substituteInPlace setup.py --replace-fail \
"version=get_version()" \
"version='${finalAttrs.version}'"
'';
build-system = with python3Packages; [ setuptools ];
doCheck = false; # No tests
buildInputs = [ glibcLocales ];
runtimeDeps = [ curl ];

View File

@@ -10,7 +10,10 @@ stdenv.mkDerivation {
pname = "jack_autoconnect";
# It does not have any versions (yet?)
version = "unstable-2021-02-01";
version = "0-unstable-2021-02-01";
strictDeps = true;
__structuredAttrs = true;
src = fetchFromGitHub {
owner = "kripton";
@@ -31,8 +34,10 @@ stdenv.mkDerivation {
];
installPhase = ''
runHook preInstall
mkdir -p -- "$out/bin"
cp -- jack_autoconnect "$out/bin"
runHook postInstall
'';
meta = {

View File

@@ -78,5 +78,6 @@ stdenv.mkDerivation rec {
];
license = lib.licenses.gpl2Only;
mainProgram = "kexec";
maintainers = [ lib.maintainers.zowoq ];
};
}

View File

@@ -23,6 +23,11 @@ stdenv.mkDerivation (finalAttrs: {
ninja
];
# Makefile builds the asm files with -Wa,--noexecstack, meson.build does not.
# If you do not pass it, you get PT_GNU_STACK=RWE on static consumers of the library.
# https://github.com/kaniini/libucontext/issues/83
env.NIX_CFLAGS_COMPILE = "-Wa,--noexecstack";
passthru.updateScript = nix-update-script { };
meta = {

View File

@@ -12,18 +12,18 @@
stdenv.mkDerivation (finalAttrs: {
pname = "matrix-sdk-crypto-nodejs";
version = "0.4.0-beta.1";
version = "0.6.6";
src = fetchFromGitHub {
owner = "matrix-org";
repo = "matrix-rust-sdk-crypto-nodejs";
rev = "v${finalAttrs.version}";
hash = "sha256-Rl0xtaEj2RnW9HPN94hjETwiMInxT1XGa1BocldQAPs=";
hash = "sha256-itTtLOLkqhcEQDmeVbYVokbTZw0xxdEi4BblIzY0iVY=";
};
cargoDeps = rustPlatform.fetchCargoVendor {
inherit (finalAttrs) pname version src;
hash = "sha256-4AC+l52I8Z3sXiViNPe6GLCl1Z+GpqjbwkcFX6BhxDA=";
hash = "sha256-sFN2V+Du7ZsN992E6btI0R5iGO9835+0z+Uxw4VzvoM=";
};
nativeBuildInputs = [
@@ -47,7 +47,7 @@ stdenv.mkDerivation (finalAttrs: {
installPhase = ''
runHook preInstall
local -r outPath="$out/lib/node_modules/@matrix-org/${finalAttrs.pname}"
local -r outPath="$out/lib/node_modules/@matrix-org/matrix-sdk-crypto-nodejs"
mkdir -p "$outPath"
cp package.json index.js index.d.ts matrix-sdk-crypto.*.node "$outPath"

View File

@@ -1,9 +1,9 @@
diff --git a/src/protections/NsfwProtection.ts b/src/protections/NsfwProtection.ts
deleted file mode 100644
index a6f45b2..0000000
index 54b6e8f..0000000
--- a/src/protections/NsfwProtection.ts
+++ /dev/null
@@ -1,115 +0,0 @@
@@ -1,118 +0,0 @@
-/*
-Copyright 2024 The Matrix.org Foundation C.I.C.
-
@@ -25,6 +25,7 @@ index a6f45b2..0000000
-import * as nsfw from "nsfwjs";
-import { LogLevel, LogService } from "@vector-im/matrix-bot-sdk";
-import { node } from "@tensorflow/tfjs-node";
-import { getMXCsInMessage } from "../utils";
-
-export class NsfwProtection extends Protection {
- settings = {};
@@ -51,76 +52,78 @@ index a6f45b2..0000000
- }
-
- public async handleEvent(mjolnir: Mjolnir, roomId: string, event: any): Promise<any> {
- if (event["type"] === "m.room.message") {
- let content = JSON.stringify(event["content"]);
- if (!content.toLowerCase().includes("mxc")) {
- return;
- }
- // try and grab a human-readable alias for more helpful management room output
- const maybeAlias = await mjolnir.client.getPublishedAlias(roomId);
- const room = maybeAlias ? maybeAlias : roomId;
- if (event.type !== "m.room.message" && event.type !== "m.sticker") {
- return;
- }
-
- const mxcs = content.match(/(mxc?:\/\/[^\s'"]+)/gim);
- if (!mxcs) {
- //something's gone wrong with the regex
- await mjolnir.managementRoomOutput.logMessage(
- LogLevel.ERROR,
- "NSFWProtection",
- `Unable to find any mxcs in ${event["event_id"]} in ${room}`,
- );
- return;
- const mxcs = getMXCsInMessage(event.content);
- if (mxcs.length <= 0) {
- return; // nothing to do
- }
-
- // try and grab a human-readable alias for more helpful management room output
- const maybeAlias = await mjolnir.client.getPublishedAlias(roomId);
- const room = maybeAlias ? maybeAlias : roomId;
-
- // Skip classification if sensitivity is 0, as it's a waste of resources
- // We are using 0.0001 as a threshold to avoid floating point errors
- if (mjolnir.config.nsfwSensitivity <= 0.0001) {
- await this.redactEvent(mjolnir, roomId, event, room);
- return;
- }
-
- for (const mxc of mxcs) {
- const image = await mjolnir.client.downloadContent(`mxc://${mxc.domain}/${mxc.mediaId}`);
-
- let decodedImage;
- try {
- decodedImage = await node.decodeImage(image.data, 3);
- } catch (e) {
- LogService.error("NsfwProtection", `There was an error processing an image: ${e}`);
- continue;
- }
-
- // @ts-ignore - see null check immediately above
- for (const mxc of mxcs) {
- const image = await mjolnir.client.downloadContent(mxc);
- const predictions = await this.model.classify(decodedImage);
-
- let decodedImage;
- try {
- decodedImage = await node.decodeImage(image.data, 3);
- } catch (e) {
- LogService.error("NsfwProtection", `There was an error processing an image: ${e}`);
- continue;
- }
-
- const predictions = await this.model.classify(decodedImage);
-
- for (const prediction of predictions) {
- if (["Hentai", "Porn"].includes(prediction["className"])) {
- if (prediction["probability"] > mjolnir.config.nsfwSensitivity) {
- try {
- await mjolnir.client.redactEvent(roomId, event["event_id"]);
- } catch (err) {
- await mjolnir.managementRoomOutput.logMessage(
- LogLevel.ERROR,
- "NSFWProtection",
- `There was an error redacting ${event["event_id"]} in ${room}: ${err}`,
- );
- }
- let eventId = event["event_id"];
- let body = `Redacted an image in ${room} ${eventId}`;
- let formatted_body = `<details>
- <summary>Redacted an image in ${room}</summary>
- <pre>${eventId}</pre> <pre>${room}</pre>
- </details>`;
- const msg = {
- msgtype: "m.notice",
- body: body,
- format: "org.matrix.custom.html",
- formatted_body: formatted_body,
- };
- await mjolnir.client.sendMessage(mjolnir.managementRoomId, msg);
- break;
- }
- for (const prediction of predictions) {
- if (["Hentai", "Porn"].includes(prediction["className"])) {
- if (prediction["probability"] > mjolnir.config.nsfwSensitivity) {
- await this.redactEvent(mjolnir, roomId, event, room);
- break;
- }
- }
- decodedImage.dispose();
- }
- decodedImage.dispose();
- }
- }
-
- private async redactEvent(mjolnir: Mjolnir, roomId: string, event: any, room: string): Promise<any> {
- try {
- await mjolnir.client.redactEvent(roomId, event["event_id"]);
- } catch (err) {
- await mjolnir.managementRoomOutput.logMessage(
- LogLevel.ERROR,
- "NSFWProtection",
- `There was an error redacting ${event["event_id"]} in ${room}: ${err}`,
- );
- }
- let eventId = event["event_id"];
- let body = `Redacted an image in ${room} ${eventId}`;
- let formatted_body = `<details>
- <summary>Redacted an image in ${room}</summary>
- <pre>${eventId}</pre> <pre>${room}</pre>
- </details>`;
- const msg = {
- msgtype: "m.notice",
- body: body,
- format: "org.matrix.custom.html",
- formatted_body: formatted_body,
- };
- await mjolnir.client.sendMessage(mjolnir.managementRoomId, msg);
- }
-}
diff --git a/src/protections/ProtectionManager.ts b/src/protections/ProtectionManager.ts
index 485f05e..6ffb0d1 100644
index bb29e40..8ec1635 100644
--- a/src/protections/ProtectionManager.ts
+++ b/src/protections/ProtectionManager.ts
@@ -31,7 +31,6 @@ import { htmlEscape } from "../utils";
@@ -129,17 +132,17 @@ index 485f05e..6ffb0d1 100644
import { LocalAbuseReports } from "./LocalAbuseReports";
-import { NsfwProtection } from "./NsfwProtection";
import { MentionSpam } from "./MentionSpam";
const PROTECTIONS: Protection[] = [
@@ -44,7 +43,6 @@ const PROTECTIONS: Protection[] = [
import { MessageIsVideo } from "./MessageIsVideo";
import { FirstMessageIsLink } from "./FirstMessageIsLink";
@@ -46,7 +45,6 @@ const PROTECTIONS: Protection[] = [
new DetectFederationLag(),
new JoinWaveShortCircuit(),
new LocalAbuseReports(),
- new NsfwProtection(),
new MentionSpam(),
];
@@ -106,9 +104,6 @@ export class ProtectionManager {
new MessageIsVideo(),
new FirstMessageIsLink(),
@@ -110,9 +108,6 @@ export class ProtectionManager {
protection.settings[key].setValue(value);
}
if (protection.enabled) {
@@ -151,31 +154,41 @@ index 485f05e..6ffb0d1 100644
}
diff --git a/test/integration/nsfwProtectionTest.ts b/test/integration/nsfwProtectionTest.ts
deleted file mode 100644
index ed215e0..0000000
index c35b2e7..0000000
--- a/test/integration/nsfwProtectionTest.ts
+++ /dev/null
@@ -1,89 +0,0 @@
@@ -1,214 +0,0 @@
-import { newTestUser } from "./clientHelper";
-
-import { MatrixClient } from "@vector-im/matrix-bot-sdk";
-import { MatrixClient, MXCUrl } from "@vector-im/matrix-bot-sdk";
-import { getFirstReaction } from "./commands/commandUtils";
-import { strict as assert } from "assert";
-import { equal } from "node:assert/strict";
-import { readFileSync } from "fs";
-import { ProtectionManager } from "../../src/protections/ProtectionManager";
-
-describe("Test: NSFW protection", function () {
- let client: MatrixClient;
- let modClient: MatrixClient;
- let spammer: MatrixClient;
- let room: string;
- this.beforeEach(async function () {
- client = await newTestUser(this.config.homeserverUrl, { name: { contains: "nsfw-protection" } });
- await client.start();
- // verify mjolnir is admin
- const admin = await this.mjolnir.isSynapseAdmin();
- if (!admin) {
- throw new Error(`Mjolnir needs to be admin for this test.`);
- }
- modClient = await newTestUser(this.config.homeserverUrl, { name: { contains: "nsfw-protection-moderator" } });
- spammer = await newTestUser(this.config.homeserverUrl, { name: { contains: "nsfw-protection-spammer" } });
- await modClient.start();
- const mjolnirId = await this.mjolnir.client.getUserId();
- room = await client.createRoom({ invite: [mjolnirId] });
- await client.joinRoom(room);
- await client.joinRoom(this.config.managementRoom);
- await client.setUserPowerLevel(mjolnirId, room, 100);
- const spammerId = await spammer.getUserId();
- room = await modClient.createRoom({ invite: [mjolnirId, spammerId] });
- await spammer.joinRoom(room);
- await modClient.joinRoom(room);
- await modClient.joinRoom(this.config.managementRoom);
- await modClient.setUserPowerLevel(mjolnirId, room, 100);
- });
- this.afterEach(async function () {
- await client.stop();
- await modClient.stop();
- });
-
- function delay(ms: number) {
@@ -185,25 +198,25 @@ index ed215e0..0000000
- it("Nsfw protection doesn't redact sfw images", async function () {
- this.timeout(20000);
-
- await client.sendMessage(this.mjolnir.managementRoomId, {
- await modClient.sendMessage(this.mjolnir.managementRoomId, {
- msgtype: "m.text",
- body: `!mjolnir rooms add ${room}`,
- });
- await getFirstReaction(client, this.mjolnir.managementRoomId, "✅", async () => {
- return await client.sendMessage(this.mjolnir.managementRoomId, {
- await getFirstReaction(modClient, this.mjolnir.managementRoomId, "✅", async () => {
- return await modClient.sendMessage(this.mjolnir.managementRoomId, {
- msgtype: "m.text",
- body: `!mjolnir enable NsfwProtection`,
- });
- });
-
- const data = readFileSync("test_tree.jpg");
- const mxc = await client.uploadContent(data, "image/png");
- const mxc = await spammer.uploadContent(data, "image/png");
- let content = { msgtype: "m.image", body: "test.jpeg", url: mxc };
- let imageMessage = await client.sendMessage(room, content);
- let imageMessage = await spammer.sendMessage(room, content);
-
- await delay(500);
- let processedImage = await client.getEvent(room, imageMessage);
- assert.equal(Object.keys(processedImage.content).length, 3, "This event should not have been redacted");
- let processedImage = await spammer.getEvent(room, imageMessage);
- equal(Object.keys(processedImage.content).length, 3, "This event should not have been redacted");
- });
-
- it("Nsfw protection redacts nsfw images", async function () {
@@ -211,21 +224,21 @@ index ed215e0..0000000
- // dial the sensitivity on the protection way up so that all images are flagged as NSFW
- this.mjolnir.config.nsfwSensitivity = 0.0;
-
- await client.sendMessage(this.mjolnir.managementRoomId, {
- await modClient.sendMessage(this.mjolnir.managementRoomId, {
- msgtype: "m.text",
- body: `!mjolnir rooms add ${room}`,
- });
- await getFirstReaction(client, this.mjolnir.managementRoomId, "✅", async () => {
- return await client.sendMessage(this.mjolnir.managementRoomId, {
- await getFirstReaction(modClient, this.mjolnir.managementRoomId, "✅", async () => {
- return await modClient.sendMessage(this.mjolnir.managementRoomId, {
- msgtype: "m.text",
- body: `!mjolnir enable NsfwProtection`,
- });
- });
-
- const data = readFileSync("test_tree.jpg");
- const mxc = await client.uploadContent(data, "image/png");
- const mxc = await spammer.uploadContent(data, "image/png");
- let content = { msgtype: "m.image", body: "test.jpeg", url: mxc };
- let imageMessage = await client.sendMessage(room, content);
- let imageMessage = await spammer.sendMessage(room, content);
-
- let formatted_body = `<img src=${mxc} />`;
- let htmlContent = {
@@ -234,13 +247,128 @@ index ed215e0..0000000
- format: "org.matrix.custom.html",
- formatted_body: formatted_body,
- };
- let htmlMessage = await client.sendMessage(room, htmlContent);
- let htmlMessage = await spammer.sendMessage(room, htmlContent);
-
- await delay(500);
- let processedImage = await client.getEvent(room, imageMessage);
- assert.equal(Object.keys(processedImage.content).length, 0, "This event should have been redacted");
- let processedImage = await modClient.getEvent(room, imageMessage);
- equal(Object.keys(processedImage.content).length, 0, "This event should have been redacted");
-
- let processedHtml = await client.getEvent(room, htmlMessage);
- assert.equal(Object.keys(processedHtml.content).length, 0, "This html image event should have been redacted");
- let processedHtml = await modClient.getEvent(room, htmlMessage);
- equal(Object.keys(processedHtml.content).length, 0, "This html image event should have been redacted");
- });
-
- it("Nsfw protection redacts nsfw images", async function () {
- this.timeout(20000);
- // dial the sensitivity on the protection way up so that all images are flagged as NSFW
- this.mjolnir.config.nsfwSensitivity = 0.0;
-
- await modClient.sendMessage(this.mjolnir.managementRoomId, {
- msgtype: "m.text",
- body: `!mjolnir rooms add ${room}`,
- });
- await getFirstReaction(modClient, this.mjolnir.managementRoomId, "✅", async () => {
- return await modClient.sendMessage(this.mjolnir.managementRoomId, {
- msgtype: "m.text",
- body: `!mjolnir enable NsfwProtection`,
- });
- });
-
- const data = readFileSync("test_tree.jpg");
- const mxc = await spammer.uploadContent(data, "image/png");
- const mediaId = MXCUrl.parse(mxc).mediaId;
- let content = { msgtype: "m.image", body: "test.jpeg", url: mxc };
- let imageMessage = await spammer.sendMessage(room, content);
-
- let formatted_body = `<img src=${mxc} />`;
- let htmlContent = {
- msgtype: "m.image",
- body: formatted_body,
- format: "org.matrix.custom.html",
- formatted_body: formatted_body,
- };
- let htmlMessage = await spammer.sendMessage(room, htmlContent);
-
- await delay(500);
- let processedImage = await modClient.getEvent(room, imageMessage);
- equal(Object.keys(processedImage.content).length, 0, "This event should have been redacted");
-
- let processedHtml = await modClient.getEvent(room, htmlMessage);
- equal(Object.keys(processedHtml.content).length, 0, "This html image event should have been redacted");
- });
-
- it("Nsfw protection does not react messages without any MXCs", async function () {
- this.timeout(20000);
-
- const protectionManager = this.mjolnir.protectionManager as ProtectionManager;
-
- // Hack our way into the protection manager to determine if it has processed an event.
- let sentEventId: string;
- const handledEventPromise = new Promise<void>((resolve) => {
- const handleEvent = protectionManager["handleEvent"].bind(protectionManager);
- protectionManager["handleEvent"] = async (roomId, event) => {
- try {
- return handleEvent(roomId, event);
- } finally {
- if (sentEventId === event.event_id) {
- resolve();
- }
- }
- };
- });
-
- await modClient.sendMessage(this.mjolnir.managementRoomId, {
- msgtype: "m.text",
- body: `!mjolnir rooms add ${room}`,
- });
-
- await getFirstReaction(modClient, this.mjolnir.managementRoomId, "✅", async () => {
- return await modClient.sendMessage(this.mjolnir.managementRoomId, {
- msgtype: "m.text",
- body: `!mjolnir enable NsfwProtection`,
- });
- });
-
- let content = { body: "This is just some text", msgtype: "m.text" };
- sentEventId = await spammer.sendMessage(room, content);
- await handledEventPromise;
- let processedEvent = await modClient.getEvent(room, sentEventId);
- equal(Object.keys(processedEvent.content).length, 2, "This event should not have been redacted");
- });
- it("Nsfw protection does not redact images from moderators", async function () {
- this.timeout(20000);
- // dial the sensitivity on the protection way up so that all images are flagged as NSFW
- this.mjolnir.config.nsfwSensitivity = 0.0;
-
- await modClient.sendMessage(this.mjolnir.managementRoomId, {
- msgtype: "m.text",
- body: `!mjolnir rooms add ${room}`,
- });
- await getFirstReaction(modClient, this.mjolnir.managementRoomId, "✅", async () => {
- return await modClient.sendMessage(this.mjolnir.managementRoomId, {
- msgtype: "m.text",
- body: `!mjolnir enable NsfwProtection`,
- });
- });
-
- const data = readFileSync("test_tree.jpg");
- const mxc = await modClient.uploadContent(data, "image/png");
- let content = { msgtype: "m.image", body: "test.jpeg", url: mxc };
- let imageMessage = await modClient.sendMessage(room, content);
-
- let formatted_body = `<img src=${mxc} />`;
- let htmlContent = {
- msgtype: "m.image",
- body: formatted_body,
- format: "org.matrix.custom.html",
- formatted_body: formatted_body,
- };
- let htmlMessage = await modClient.sendMessage(room, htmlContent);
-
- await delay(500);
- let processedImage = await modClient.getEvent(room, imageMessage);
- equal(Object.keys(processedImage.content).length, 3, "This event should not have been redacted");
-
- let processedHtml = await modClient.getEvent(room, htmlMessage);
- equal(Object.keys(processedHtml.content).length, 4, "This html image event should not have been redacted");
- });
-});

View File

@@ -4,7 +4,7 @@
yarnConfigHook,
yarnBuildHook,
yarnInstallHook,
nodejs,
nodejs_22,
fetchFromGitHub,
fetchYarnDeps,
matrix-sdk-crypto-nodejs,
@@ -14,13 +14,13 @@
stdenv.mkDerivation (finalAttrs: {
pname = "mjolnir";
version = "1.9.2";
version = "1.12.1";
src = fetchFromGitHub {
owner = "matrix-org";
repo = "mjolnir";
tag = "v${finalAttrs.version}";
hash = "sha256-OxHnCMP6IP0EaAs4YQgmV04tq6IdAYmKQX8O9Q48CPk=";
hash = "sha256-PWPtp1KVOBNH7lu99Yy3hmj8wGOZe+YKjPq/SyO7oLM=";
};
patches = [
@@ -30,14 +30,14 @@ stdenv.mkDerivation (finalAttrs: {
offlineCache = fetchYarnDeps {
yarnLock = "${finalAttrs.src}/yarn.lock";
hash = "sha256-1V7ooONt9j+4hk/3w6Dsv/SdWwa1xsLk97EwhuPegNo=";
hash = "sha256-M4gsuzSxKOIDPL4J2HnveRDjviB0RPBmLVYBlTVz788=";
};
nativeBuildInputs = [
yarnConfigHook
yarnBuildHook
yarnInstallHook
nodejs
nodejs_22
makeWrapper
];
@@ -47,7 +47,7 @@ stdenv.mkDerivation (finalAttrs: {
rm -rf $out/lib/node_modules/mjolnir/node_modules/@matrix-org/matrix-sdk-crypto-nodejs
ln -s ${matrix-sdk-crypto-nodejs}/lib/node_modules/@matrix-org/matrix-sdk-crypto-nodejs $out/lib/node_modules/mjolnir/node_modules/@matrix-org/matrix-sdk-crypto-nodejs
makeWrapper ${nodejs}/bin/node "$out/bin/mjolnir" \
makeWrapper ${nodejs_22}/bin/node "$out/bin/mjolnir" \
--add-flags "$out/lib/node_modules/mjolnir/lib/index.js"
'';

View File

@@ -11,7 +11,10 @@ let
in
stdenv.mkDerivation {
pname = "mmh";
version = "unstable-2023-09-24";
version = "0.4-unstable-2023-09-24";
strictDeps = true;
__structuredAttrs = true;
src = fetchurl {
url = "http://git.marmaro.de/?p=mmh;a=snapshot;h=${rev};sf=tgz";

View File

@@ -6,16 +6,16 @@
buildNpmPackage (finalAttrs: {
pname = "mongosh";
version = "2.11.1";
version = "2.12.0";
src = fetchFromGitHub {
owner = "mongodb-js";
repo = "mongosh";
tag = "v${finalAttrs.version}";
hash = "sha256-h1OUm4fPYdDpU1K1a65Q5xeBHEryA1O05k0wr/x/yUQ=";
hash = "sha256-P6gT2+cFuPYc3oN2O0h/83Tz7J65tQfD92GDLBybY3M=";
};
npmDepsHash = "sha256-/pHYIFybLfpj5B88T+B1stDnwMEOBIhIRX83ipSIAvo=";
npmDepsHash = "sha256-UhSze1kTMzJ2OuEEn6D0oTtuV5titsaFrWS/Gm1HJtU=";
postPatch = ''
# Disable telemetry by default; users can still opt in via enableTelemetry().

View File

@@ -8,7 +8,7 @@
}:
buildGoModule (finalAttrs: {
pname = "nerdlog";
version = "1.11.0";
version = "1.12.0";
__structuredAttrs = true;
@@ -16,10 +16,10 @@ buildGoModule (finalAttrs: {
owner = "dimonomid";
repo = "nerdlog";
tag = "v${finalAttrs.version}";
hash = "sha256-jKOpFPLqRy4aU3RTEloX+RjFTW0E65XbbL/uSMRHyJA=";
hash = "sha256-Q478aeetAu+lWJYCn3IE4anpghNXRazlFIKPXf+hEhA=";
};
vendorHash = "sha256-D/1iKXTJuV9RM4IbC/FmpxJDIaBDBts1GEO8YyCGq7A=";
vendorHash = "sha256-joQY9gJiyw0fit6bp0gHZ31VxQ4+qHlqeOr/fXfnDPg=";
buildInputs = [ libx11 ];

View File

@@ -6,13 +6,13 @@
buildGo127Module (finalAttrs: {
pname = "nerva";
version = "1.70.0";
version = "1.70.1";
src = fetchFromGitHub {
owner = "praetorian-inc";
repo = "nerva";
tag = "v${finalAttrs.version}";
hash = "sha256-WKGcn1F2uF5vUVVL8oBDmBG5434bLXzCyyFL95Y3t/Y=";
hash = "sha256-8XEoNhczrDQ2vrgimfYxJ3jQFGsZmfM7vWTa6vfOmW0=";
};
vendorHash = "sha256-Fjxs+JKq9Lv9wBQEjvSlEw9cpgm/Ye1l4iqdjRa8+X8=";

View File

@@ -2,7 +2,6 @@
lib,
fetchFromGitHub,
python3,
fetchpatch2,
plugins ? _ps: [ ],
nixosTests,
nix-update-script,
@@ -21,24 +20,18 @@ py.pkgs.buildPythonApplication (finalAttrs: {
__structuredAttrs = true;
pname = "netbox";
version = "4.6.8";
version = "4.7.1";
pyproject = false;
src = fetchFromGitHub {
owner = "netbox-community";
repo = "netbox";
tag = "v${finalAttrs.version}";
hash = "sha256-fhEcQBYL5R9Tv9CpAf3Ce1oIzsXCjtH5j+dP9sD6kdg=";
hash = "sha256-6IJrDD+1yBv15cbJwZOLkwiAlGZH2zRC+a/CG79C10Y=";
};
patches = [
./custom-static-root.patch
# TODO: remove before upgrading to NetBox v4.7
(fetchpatch2 {
name = "upgrade-django-tables2-v3.0.patch";
url = "https://github.com/netbox-community/netbox/commit/d57346d9f0eef8126eafcd5033ea43864faeaf0d.patch";
hash = "sha256-6/wdd8wDVT4eqDKMNx8tmoPTDvw8OE7atf9nzg3LZzk=";
})
];
dependencies =
@@ -50,10 +43,9 @@ py.pkgs.buildPythonApplication (finalAttrs: {
django-cors-headers
django-debug-toolbar
django-filter
django-graphiql-debug-toolbar
django-htmx
django-mptt
django-pglocks
django-pgware
django-prometheus
django-redis
django-rq

View File

@@ -2,14 +2,19 @@
lib,
stdenv,
fetchFromGitHub,
installShellFiles,
expat,
zlib,
versionCheckHook,
}:
stdenv.mkDerivation (finalAttrs: {
pname = "newflasher";
version = "61";
strictDeps = true;
__structuredAttrs = true;
src = fetchFromGitHub {
owner = "munjeni";
repo = "newflasher";
@@ -17,6 +22,8 @@ stdenv.mkDerivation (finalAttrs: {
hash = "sha256-9qEGFzA5sMn+1MOKNTJeBukurzytksXitgXraPL0KDU=";
};
nativeBuildInputs = [ installShellFiles ];
buildInputs = [
expat
zlib
@@ -24,10 +31,14 @@ stdenv.mkDerivation (finalAttrs: {
installPhase = ''
runHook preInstall
install -Dm755 newflasher $out/bin/newflasher
installBin newflasher
installManPage newflasher.1
runHook postInstall
'';
nativeInstallCheckInputs = [ versionCheckHook ];
doInstallCheck = true;
meta = {
description = "Flash tool for new Sony flash tool protocol (Xperia XZ Premium and newer)";
homepage = "https://github.com/munjeni/newflasher";

View File

@@ -49,13 +49,13 @@ let
in
buildGoModule (finalAttrs: {
pname = "nezha";
version = "2.3.12";
version = "2.3.14";
src = fetchFromGitHub {
owner = "nezhahq";
repo = "nezha";
tag = "v${finalAttrs.version}";
hash = "sha256-XgTbDpfGYbpiFseJjwraDg3svyT0EpgvoY2dvLbtZtQ=";
hash = "sha256-xWJfTop9U3Ms5oeTD4Sdn6ZmESjrx5H9WnAn9KJq608=";
};
proxyVendor = true;

View File

@@ -5,7 +5,7 @@ from argparse import Namespace
from dataclasses import dataclass
from enum import Enum
from pathlib import Path
from typing import Any, ClassVar, Self, TypedDict, override
from typing import Any, ClassVar, NotRequired, Self, TypedDict, override
from . import nix
from .process import Remote, run_wrapper
@@ -170,6 +170,14 @@ class FlakeMetadataJson(TypedDict):
resolvedUrl: str
class NixOSVersionJson(TypedDict):
# Keys are NotRequired here so we need to parse them safely
nixosVersion: NotRequired[str]
configurationRevision: NotRequired[str]
kernelVersion: NotRequired[str]
specialisations: NotRequired[list[str]]
@dataclass(frozen=True)
class GroupedNixArgs:
build_flags: Args

View File

@@ -24,6 +24,7 @@ from .models import (
GenerationJson,
ImageVariants,
NixOSRebuildError,
NixOSVersionJson,
Profile,
Remote,
)
@@ -511,37 +512,25 @@ def list_generations(profile: Profile) -> list[GenerationJson]:
generation_path = (
profile.path.parent / f"{profile.path.name}-{generation.id}-link"
)
j: NixOSVersionJson
try:
nixos_version = (generation_path / "nixos-version").read_text().strip()
except OSError as ex:
logger.debug("could not get nixos-version: %s", ex)
nixos_version = "Unknown"
try:
kernel_version = next(
(generation_path / "kernel-modules/lib/modules").iterdir()
).name
except OSError as ex:
logger.debug("could not get kernel version: %s", ex)
kernel_version = "Unknown"
specialisations = [
s.name for s in (generation_path / "specialisation").glob("*") if s.is_dir()
]
try:
configuration_revision = run_wrapper(
[generation_path / "sw/bin/nixos-version", "--configuration-revision"],
result = run_wrapper(
[generation_path / "sw/bin/nixos-version", "--json"],
capture_output=True,
).stdout.strip()
except (OSError, CalledProcessError) as ex:
).stdout
j = json.loads(result)
except (OSError, CalledProcessError, json.JSONDecodeError) as ex:
logger.debug("could not get configuration revision: %s", ex)
configuration_revision = "Unknown"
j = {}
return GenerationJson(
generation=generation.id,
date=generation.timestamp,
nixosVersion=nixos_version,
kernelVersion=kernel_version,
configurationRevision=configuration_revision,
specialisations=specialisations,
nixosVersion=j.get("nixosVersion", "Unknown"),
kernelVersion=j.get("kernelVersion", "Unknown"),
configurationRevision=j.get("configurationRevision", "Unknown"),
specialisations=j.get("specialisations", []),
current=generation.current,
)

View File

@@ -3,7 +3,7 @@ import sys
import textwrap
import uuid
from pathlib import Path
from subprocess import PIPE, CompletedProcess
from subprocess import PIPE, CalledProcessError, CompletedProcess
from typing import Any
from unittest.mock import ANY, Mock, call, patch
@@ -584,9 +584,71 @@ def test_get_generations_from_nix_env(tmp_path: Path) -> None:
),
],
)
def test_list_generations(mock_get_generations: Mock, tmp_path: Path) -> None:
# Probably better to test this function in a real system, this test is
# mostly to make sure it doesn't break horribly
@patch(get_qualified_name(n.run_wrapper, n), autospec=True)
def test_list_generations(
mock_run: Mock,
mock_get_generations: Mock,
tmp_path: Path,
) -> None:
# happy path
mock_run.return_value = CompletedProcess(
args=[],
returncode=0,
stdout=json.dumps(
{
"configurationRevision": "3f0180ea99a4c8277961825ec5fca2b50a0eca75",
"kernelVersion": "7.2.8",
"nixosVersion": "26.11.20260925.e94cb15",
"specialisations": ["foo", "bar"],
}
),
)
assert n.list_generations(m.Profile("system", tmp_path)) == [
{
"configurationRevision": "3f0180ea99a4c8277961825ec5fca2b50a0eca75",
"current": True,
"date": "2024-11-07 23:54:17",
"generation": 2,
"kernelVersion": "7.2.8",
"nixosVersion": "26.11.20260925.e94cb15",
"specialisations": ["foo", "bar"],
},
{
"configurationRevision": "3f0180ea99a4c8277961825ec5fca2b50a0eca75",
"current": False,
"date": "2024-11-07 23:54:17",
"generation": 1,
"kernelVersion": "7.2.8",
"nixosVersion": "26.11.20260925.e94cb15",
"specialisations": ["foo", "bar"],
},
]
# parsing invalid JSON
mock_run.return_value = CompletedProcess(args=[], returncode=0, stdout="garbage")
assert n.list_generations(m.Profile("system", tmp_path)) == [
{
"configurationRevision": "Unknown",
"current": True,
"date": "2024-11-07 23:54:17",
"generation": 2,
"kernelVersion": "Unknown",
"nixosVersion": "Unknown",
"specialisations": [],
},
{
"configurationRevision": "Unknown",
"current": False,
"date": "2024-11-07 23:54:17",
"generation": 1,
"kernelVersion": "Unknown",
"nixosVersion": "Unknown",
"specialisations": [],
},
]
# error calling nixos-version
mock_run.side_effect = CalledProcessError(returncode=1, cmd=[])
assert n.list_generations(m.Profile("system", tmp_path)) == [
{
"configurationRevision": "Unknown",

View File

@@ -29,7 +29,10 @@ rustPlatform.buildRustPackage (finalAttrs: {
homepage = "https://github.com/gregl83/paq";
changelog = "https://github.com/gregl83/paq/releases/tag/v${finalAttrs.version}";
license = lib.licenses.mit;
maintainers = with lib.maintainers; [ lafrenierejm ];
maintainers = with lib.maintainers; [
gregl83
lafrenierejm
];
mainProgram = "paq";
};
})

View File

@@ -10,14 +10,14 @@
buildGoModule (finalAttrs: {
pname = "pgschema";
version = "1.13.0";
version = "1.13.1";
__structuredAttrs = true;
src = fetchFromGitHub {
owner = "pgplex";
repo = "pgschema";
tag = "v${finalAttrs.version}";
hash = "sha256-w1MLl9NFAS+7a1CzS5IMQUw6BzL8sifNPdnStLySFVg=";
hash = "sha256-hSS+S16LidfSEaSJPkJiaDTdtjaJS1h3wORqpZYyT44=";
};
# Adapted from $src/nix/pgschema.nix

View File

@@ -14,6 +14,7 @@
python3,
cacert,
writableTmpDirAsHomeHook,
fetchpatch2,
}:
stdenv.mkDerivation (finalAttrs: {
@@ -32,6 +33,14 @@ stdenv.mkDerivation (finalAttrs: {
patches = [
# https://github.com/OSGeo/PROJ/pull/3252
./only-add-curl-for-static-builds.patch
# Unbreak mapnik
(fetchpatch2 {
name = "fix_issue_with_target_compile_features.patch";
# https://github.com/OSGeo/PROJ/pull/4863
url = "https://github.com/OSGeo/PROJ/commit/7ea0fd3ba479845464b34ccf5265b8e6d055cde5.patch?full_index=1";
hash = "sha256-IIe0T1/8Jv7tvhUupFn46PaFi7zggAT79EC55cmxHSs=";
})
];
outputs = [

View File

@@ -8,7 +8,7 @@
rustPlatform.buildRustPackage (finalAttrs: {
pname = "rainfrog";
version = "0.4.5";
version = "0.4.6";
__structuredAttrs = true;
@@ -16,10 +16,10 @@ rustPlatform.buildRustPackage (finalAttrs: {
owner = "achristmascarl";
repo = "rainfrog";
tag = "v${finalAttrs.version}";
hash = "sha256-kA3rIGmSid3qbIasqoSnFv4w0P+RrAWoH8PszY9xSGs=";
hash = "sha256-fcQFHUw1+h1PqmPlanvcFsudUb9nePZA0yJaFOwvx3U=";
};
cargoHash = "sha256-A3gZF2oJVt5WR56JVwsPOVvgu/d9veD01+gQESNV0Qc=";
cargoHash = "sha256-IXbPCxz+plIa6jYMTRcG44e7sHmwxzA+lbtWb/ukzcU=";
nativeInstallCheckInputs = [ versionCheckHook ];
doInstallCheck = true;

View File

@@ -10,14 +10,14 @@
rustPlatform.buildRustPackage (finalAttrs: {
pname = "rusthound-ce";
version = "2.5.13";
version = "2.5.14";
src = fetchCrate {
inherit (finalAttrs) pname version;
hash = "sha256-YDW7tL37rKOm+PU98NhtimirVLla40dLx2VGOLfDVho=";
hash = "sha256-VJFwR/iGAdNazZBEkyPfYgW9gDfPJR0xa3LhtiJ4cLg=";
};
cargoHash = "sha256-5z7VBRua+plcMOf1kY8MxYKPbmKlo4yVEz8WzX6oVWA=";
cargoHash = "sha256-q1NwitdAHgP6LmQ6SgKD8CnNNoyaoUha2v1edUp6Jbc=";
nativeBuildInputs = [
pkg-config

View File

@@ -20,13 +20,13 @@ stdenvNoCC.mkDerivation (finalAttrs: {
strictDeps = true;
pname = "sable-unwrapped";
version = "1.22.6";
version = "1.22.9";
src = fetchFromGitHub {
owner = "SableClient";
repo = "Sable";
tag = "v${finalAttrs.version}";
hash = "sha256-AZ2gKcCLJvllC/lOL+l/zt3/RxztWd8mu25Eo0vyMBY=";
hash = "sha256-TZycPD+lor6pCTcJaZLUvb84CyFn5jBROltdz9hSdLg=";
};
pnpmDeps = fetchPnpmDeps {

View File

@@ -15,13 +15,13 @@
}:
rustPlatform.buildRustPackage (finalAttrs: {
pname = "snx-rs";
version = "6.3.1";
version = "6.4.1";
src = fetchFromGitHub {
owner = "ancwrd1";
repo = "snx-rs";
tag = "v${finalAttrs.version}";
hash = "sha256-2cRTD3fVn8Ko5nZ3L+/hsXOT8Gc91hk6+0mvxLKMa08=";
hash = "sha256-J0wjLavv6OCdYzIMsRnQEoK4OJU68QLvTbIL4hTOOCU=";
};
passthru.updateScript = nix-update-script { };
@@ -49,7 +49,7 @@ rustPlatform.buildRustPackage (finalAttrs: {
versionCheckHook
];
cargoHash = "sha256-Qvx8bb2Mr8UQYrk++wOoDqqAe/BA0LlbQUS8Y+TCYNo=";
cargoHash = "sha256-nQ2lQbPaK0rZE3XscSBgeceIHqLAFxeU9agBoPlffBI=";
doInstallCheck = true;
versionCheckProgram = "${placeholder "out"}/bin/snx-rs";

View File

@@ -35,18 +35,18 @@ let
steelix-unwrapped = helix-unwrapped.overrideAttrs (
finalAttrs: _: {
pname = "steelix-unwrapped";
version = "0-unstable-2026-05-21";
version = "0-unstable-2026-09-26";
src = fetchFromGitHub {
owner = "mattwparas";
repo = "helix";
rev = "4d86612df48447088ef4190bf503fd54a7562aa9";
hash = "sha256-qAUODNxHM9K6CrRCFgfBcbqzRd+YHiWn9fEfmIzrohA=";
rev = "df595c7dc5729e2712c79dd2e35977e3474b3ec6";
hash = "sha256-zWOzgArhg4PCgi8AMKLtcttBtchlQJay6atEpobCASk=";
};
cargoDeps = rustPlatform.fetchCargoVendor {
inherit (finalAttrs) src pname version;
hash = "sha256-6bu8sIM4So3AbnHHYbh8uu+rEB4IjMQjDgh7/AkLQs0=";
hash = "sha256-h4HkOppmseHzX1gHUGO1XSwrg4uCJ4PjmI/3WsYp2C4=";
};
cargoBuildFlags = [

View File

@@ -2260,10 +2260,10 @@
};
php-only = {
version = "0.24.2-unstable-2026-03-19";
version = "0.25.0-unstable-2026-09-24";
url = "github:tree-sitter/tree-sitter-php";
rev = "3f2465c217d0a966d41e584b42d75522f2a3149e";
hash = "sha256-RV6wHYVTOFdRYMqXdPw2Ryk3FadJJ4jcJVFjsJG8Ri0=";
rev = "92b5271b60bec77fb65b5e5bc41561e8dac81299";
hash = "sha256-EkKYb9jatSl0/o+7tO2O3vx44ufDmZ4YJ/6t4il/Yk0=";
meta = {
license = lib.licenses.mit;
maintainers = with lib.maintainers; [
@@ -2290,10 +2290,10 @@
};
pkl = {
version = "0.20.0-unstable-2026-03-27";
version = "0.21.0-unstable-2026-09-25";
url = "github:apple/tree-sitter-pkl";
rev = "f5beed1da8e5fc856a1a11e29a929d0b7cdcfe3c";
hash = "sha256-q0K+q8GEOiwbgFjA/jiY/Hg6kPlgqMUvH8g+GdEDU3I=";
rev = "c95d8284940f5e1da2cd0d8f1ee45d7ef9ef75d1";
hash = "sha256-dnGqTZ7Kga1sIJkzRSbqkhvIrPJMxOEhHnDKJuLyudM=";
meta = {
license = lib.licenses.asl20;
maintainers = with lib.maintainers; [
@@ -2910,9 +2910,9 @@
};
sshclientconfig = rec {
version = "2026.8.27";
version = "2026.9.24";
url = "github:metio/tree-sitter-ssh-client-config?ref=${version}";
hash = "sha256-yTdEinKdEmWPiw6+fBq15tXe8GsoC7PFk2pVaDSFCYA=";
hash = "sha256-M5PwCbNxs8Ow5YZl178PLJy3Lq9vxm9PEDvsR5UVJHE=";
meta = {
license = lib.licenses.cc0;
maintainers = with lib.maintainers; [

View File

@@ -66,7 +66,7 @@
stdenv.mkDerivation rec {
pname = "vivaldi";
version = "8.2.4133.52";
version = "8.2.4133.76";
suffix =
{
@@ -79,8 +79,8 @@ stdenv.mkDerivation rec {
url = "https://downloads.vivaldi.com/stable/vivaldi-stable_${version}-1_${suffix}.deb";
hash =
{
aarch64-linux = "sha256-5v9DCL6B8JnZrFoniAFg5fpLD1ojOnT7HIt4HuQZJzI=";
x86_64-linux = "sha256-QOXpNQULSr7dR98o2AODBHMbvw/3NhqXlh1iB6i8rQM=";
aarch64-linux = "sha256-co8BxKbDVyjYWOEIg4MOHiNB1GnHQjm8Z9nWabyaivA=";
x86_64-linux = "sha256-WPfZYy+cCxSKFdLbD5MpTb4lovGk0nDVRcjpXxbOwGI=";
}
.${stdenv.hostPlatform.system} or (throw "Unsupported system: ${stdenv.hostPlatform.system}");
};

View File

@@ -0,0 +1,67 @@
{
lib,
rustPlatform,
fetchFromGitHub,
cmake,
installShellFiles,
llvmPackages,
pkg-config,
alsa-lib,
libxkbcommon,
nix-update-script,
versionCheckHook,
}:
rustPlatform.buildRustPackage (finalAttrs: {
pname = "whisrs";
version = "0.1.27";
__structuredAttrs = true;
src = fetchFromGitHub {
owner = "y0sif";
repo = "whisrs";
tag = "v${finalAttrs.version}";
hash = "sha256-sBBxtq1YXKbYoXtaEfoUWLJjfVgGKrfnXbPXYE798tQ=";
};
cargoHash = "sha256-Us7WkzNUPYCwv+UfdEwkZe9Xdk9ejwnEJ8t8ZPEWujA=";
nativeBuildInputs = [
cmake
installShellFiles
llvmPackages.clang
pkg-config
rustPlatform.bindgenHook
];
buildInputs = [
alsa-lib
libxkbcommon
];
# contrib/99-whisrs.rules is deliberately not installed: it grants /dev/uinput
# to the `input` group and would override the `uinput` group set by NixOS's
# hardware.uinput.enable. Users should enable that option instead.
postInstall = ''
installManPage contrib/whisrs.1 contrib/whisrsd.1
install -Dm644 contrib/whisrs.service -t $out/lib/systemd/user
'';
versionCheckProgram = "${placeholder "out"}/bin/whisrsd";
nativeInstallCheckInputs = [ versionCheckHook ];
doInstallCheck = true;
passthru.updateScript = nix-update-script { };
meta = {
description = "Voice-to-text dictation daemon that types transcriptions into the focused window";
homepage = "https://github.com/y0sif/whisrs";
license = lib.licenses.mit;
mainProgram = "whisrs";
maintainers = with lib.maintainers; [ otavio ];
platforms = lib.platforms.linux;
};
})

View File

@@ -73,14 +73,14 @@
lowerBoundSatisfied && upperBoundSatisfied;
/**
Generates a CUDA variant name from a version.
Generates CUDA variant names from a version.
NOTE: No guarantees are made about this function's stability. You may use it at your own risk.
# Type
```
_mkCudaVariant :: (version :: String) -> String
_mkCudaVariants :: (version :: String) -> [ String ]
```
# Inputs
@@ -92,15 +92,21 @@
# Examples
:::{.example}
## `_cuda.lib._mkCudaVariant` usage examples
## `_cuda.lib._mkCudaVariants` usage examples
```nix
_mkCudaVariant "11.0"
=> "cuda11"
_mkCudaVariants "13.2.2"
=> [ "cuda13.2.2" "cuda13.2" "cuda13" ]
```
:::
*/
_mkCudaVariant = version: "cuda${lib.versions.major version}";
_mkCudaVariants =
cudaMajorMinorPatchVersion:
lib.map (f: "cuda" + (f cudaMajorMinorPatchVersion)) [
lib.id
lib.versions.majorMinor
lib.versions.major
];
/**
A predicate which, given a package, returns true if the package has a free license or one of NVIDIA's licenses.

View File

@@ -14,7 +14,7 @@
inherit (import ./cuda.nix { inherit _cuda lib; })
_cudaCapabilityIsDefault
_cudaCapabilityIsSupported
_mkCudaVariant
_mkCudaVariants
allowUnfreeCudaPredicate
;

View File

@@ -6,6 +6,7 @@
autoAddDriverRunpath,
autoPatchelfHook,
backendStdenv,
cudaMajorMinorPatchVersion,
cudaMajorMinorVersion,
cudaMajorVersion,
cudaNamePrefix,
@@ -21,7 +22,7 @@
}:
let
inherit (backendStdenv) hostRedistSystem;
inherit (_cuda.lib) getNixSystems _mkCudaVariant mkRedistUrl;
inherit (_cuda.lib) getNixSystems _mkCudaVariants mkRedistUrl;
inherit (lib.attrsets)
foldlAttrs
getDev
@@ -70,7 +71,7 @@ let
getSupportedReleases =
let
desiredCudaVariant = _mkCudaVariant cudaMajorVersion;
desiredCudaVariants = _mkCudaVariants cudaMajorMinorPatchVersion;
in
release:
# Always show preference to the "source", then "linux-all" redistSystem if they are available, as they are
@@ -92,9 +93,16 @@ let
acc
# If the value is an attribute, and when hasCudaVariants is true it has the relevant CUDA variant,
# then add it to the set.
// optionalAttrs (isAttrs value && (hasCudaVariants -> hasAttr desiredCudaVariant value)) {
${name} = value.${desiredCudaVariant} or value;
}
// (
let
desiredCudaVariant = findFirst (
variant: isAttrs value && hasAttr variant value
) null desiredCudaVariants;
in
optionalAttrs (isAttrs value && (hasCudaVariants -> desiredCudaVariant != null)) {
${name} = if desiredCudaVariant == null then value else value.${desiredCudaVariant};
}
)
) { } release;
getPreferredRelease =

View File

@@ -146,6 +146,7 @@ let
inherit (finalCudaPackages)
autoAddCudaCompatRunpath
backendStdenv
cudaMajorMinorPatchVersion
cudaMajorMinorVersion
cudaMajorVersion
cudaNamePrefix

View File

@@ -1,4 +1,10 @@
{ buildRedist }:
{
buildRedist,
config,
lib,
openssl,
stdenv,
}:
buildRedist {
redistName = "cuda";
pname = "cuda_compat";
@@ -7,14 +13,29 @@ buildRedist {
# To avoid that (and troubleshooting why), we just use a single output.
outputs = [ "out" ];
# libnvidia-pkcs11{-openssl3}.so is only shipped on x86_64-linux
buildInputs = lib.optionals stdenv.hostPlatform.isx86_64 [
openssl
];
autoPatchelfIgnoreMissingDeps = [
"libnvdla_runtime.so"
"libnvrm_gpu.so"
"libnvrm_mem.so"
]
++ lib.optionals stdenv.hostPlatform.isx86_64 [
# Used by libnvidia-pkcs11.so but openssl_1_1 has been removed from nixpkgs (EoL)
"libcrypto.so.1.1"
];
meta = {
description = "Provides minor version forward compatibility for the CUDA runtime";
homepage = "https://docs.nvidia.com/deploy/cuda-compatibility";
problems = lib.optionalAttrs (!config.enableCudaDriverCompat) {
cuda-compat-disabled = {
kind = "broken";
message = "cuda_compat must be explicitly enabled using config.enableCudaDriverCompat.";
};
};
};
}

View File

@@ -13,5 +13,6 @@ buildRedist {
ptx text from host binaries.
'';
homepage = "https://docs.nvidia.com/cuda/cuda-binary-utilities#cuobjdump";
mainProgram = "cuobjdump";
};
}

View File

@@ -14,5 +14,6 @@ buildRedist {
also does control flow analysis to annotate jump/branch targets and makes the output easier to read.
'';
homepage = "https://docs.nvidia.com/cuda/cuda-binary-utilities#nvdisasm";
mainProgram = "nvdisasm";
};
}

View File

@@ -0,0 +1,56 @@
{
lib,
buildDunePackage,
fetchFromGitHub,
alcotest,
astring,
capnp,
capnproto,
eio,
fmt,
logs,
stdint,
uri,
}:
buildDunePackage (finalAttrs: {
pname = "capnp-rpc";
version = "2.1.2-unstable-2026-09-13";
minimalOCamlVersion = "5.2";
src = fetchFromGitHub {
owner = "mirage";
repo = "capnp-rpc";
rev = "256ad12f21931f04eb88ad4d5cc966b7829bd906";
hash = "sha256-zibjsyp4Vin0sZrwWio+h/88bdsVfmFEA7aPmc1IRg0=";
};
nativeBuildInputs = [
capnp
capnproto
];
propagatedBuildInputs = [
astring
capnp
fmt
logs
eio
stdint
uri
];
checkInputs = [
alcotest
];
doCheck = true;
meta = {
description = "Cap'n Proto RPC library for OCaml";
homepage = "https://github.com/mirage/capnp-rpc";
changelog = "https://github.com/mirage/capnp-rpc/blob/v${finalAttrs.version}/CHANGES.md";
license = lib.licenses.asl20;
};
})

View File

@@ -0,0 +1,53 @@
{
buildDunePackage,
asn1-combinators,
astring,
base64,
capnp,
capnp-rpc,
capnproto,
cstruct,
fmt,
logs,
mirage-crypto,
mirage-crypto-rng,
prometheus,
ptime,
tls-eio,
uri,
x509,
}:
buildDunePackage {
pname = "capnp-rpc-net";
minimalOCamlVersion = "5.2";
inherit (capnp-rpc) src version;
nativeBuildInputs = [
capnproto
];
propagatedBuildInputs = [
asn1-combinators
astring
base64
capnp
capnp-rpc
cstruct
fmt
logs
mirage-crypto
mirage-crypto-rng
prometheus
ptime
tls-eio
uri
x509
];
meta = capnp-rpc.meta // {
description = "Network and TLS support for Cap'n Proto RPC services";
};
}

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