mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-09-29 19:30:11 +00:00
Merge staging-next into staging
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
;;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
'')
|
||||
|
||||
@@ -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.";
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
'')
|
||||
];
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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'")
|
||||
'';
|
||||
}
|
||||
@@ -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}",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
{
|
||||
name = "kexec";
|
||||
meta = with lib.maintainers; {
|
||||
maintainers = [
|
||||
maintainers = pkgs.kexec-tools.meta.maintainers ++ [
|
||||
flokli
|
||||
lassulus
|
||||
];
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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'")
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ lib, pkgs, ... }:
|
||||
{
|
||||
name = "syncthing";
|
||||
name = "syncthing-no-settings";
|
||||
meta.maintainers = with pkgs.lib.maintainers; [ chkno ];
|
||||
|
||||
nodes = {
|
||||
|
||||
11
nixos/tests/syncthing/test-nodes/a/cert.pem
Normal file
11
nixos/tests/syncthing/test-nodes/a/cert.pem
Normal 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-----
|
||||
1
nixos/tests/syncthing/test-nodes/a/id
Normal file
1
nixos/tests/syncthing/test-nodes/a/id
Normal file
@@ -0,0 +1 @@
|
||||
F2ACIUG-FML5RHY-ATV55ZN-5KGVUIV-RWFG3Y6-QPLTKZB-NWUZAGD-7QRCWAP
|
||||
3
nixos/tests/syncthing/test-nodes/a/key.pem
Normal file
3
nixos/tests/syncthing/test-nodes/a/key.pem
Normal file
@@ -0,0 +1,3 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MC4CAQAwBQYDK2VwBCIEIE2ls259KcQgtizG7hwP3aBhlYBNuPJwSBG8d4uVBFOh
|
||||
-----END PRIVATE KEY-----
|
||||
11
nixos/tests/syncthing/test-nodes/b/cert.pem
Normal file
11
nixos/tests/syncthing/test-nodes/b/cert.pem
Normal file
@@ -0,0 +1,11 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBnzCCAVGgAwIBAgIIKyLKAcqLWn4wBQYDK2VwMEoxEjAQBgNVBAoTCVN5bmN0
|
||||
aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0ZWQxEjAQBgNVBAMT
|
||||
CXN5bmN0aGluZzAeFw0yNjA0MDIwMDAwMDBaFw00NjAzMjgwMDAwMDBaMEoxEjAQ
|
||||
BgNVBAoTCVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0
|
||||
ZWQxEjAQBgNVBAMTCXN5bmN0aGluZzAqMAUGAytlcAMhABoahydRmwpbCII6mz9i
|
||||
E8FeGH7YdHqgMeAmLiFZu2wMo1UwUzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYw
|
||||
FAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwFAYDVR0RBA0wC4IJ
|
||||
c3luY3RoaW5nMAUGAytlcANBADFVKB2vF16j0gc/h71x3vUi042FbmXTPk9kMb2O
|
||||
9O0NnWSoMuOGPFDEoHWBFuUuixQ/3cw45zw+fea28m95gQo=
|
||||
-----END CERTIFICATE-----
|
||||
1
nixos/tests/syncthing/test-nodes/b/id
Normal file
1
nixos/tests/syncthing/test-nodes/b/id
Normal file
@@ -0,0 +1 @@
|
||||
LOB4D2H-OYG64QZ-NDX43LJ-NYR4HQU-BRNXCNI-ERACWFP-OVURLBI-63MR5QP
|
||||
3
nixos/tests/syncthing/test-nodes/b/key.pem
Normal file
3
nixos/tests/syncthing/test-nodes/b/key.pem
Normal file
@@ -0,0 +1,3 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MC4CAQAwBQYDK2VwBCIEIJtOML1Tshk03rB41IX5ajEeem50CdWzHDimotheTyZi
|
||||
-----END PRIVATE KEY-----
|
||||
11
nixos/tests/syncthing/test-nodes/c/cert.pem
Normal file
11
nixos/tests/syncthing/test-nodes/c/cert.pem
Normal file
@@ -0,0 +1,11 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBoDCCAVKgAwIBAgIJAIZk5+sv3EbTMAUGAytlcDBKMRIwEAYDVQQKEwlTeW5j
|
||||
dGhpbmcxIDAeBgNVBAsTF0F1dG9tYXRpY2FsbHkgR2VuZXJhdGVkMRIwEAYDVQQD
|
||||
EwlzeW5jdGhpbmcwHhcNMjYwNDAyMDAwMDAwWhcNNDYwMzI4MDAwMDAwWjBKMRIw
|
||||
EAYDVQQKEwlTeW5jdGhpbmcxIDAeBgNVBAsTF0F1dG9tYXRpY2FsbHkgR2VuZXJh
|
||||
dGVkMRIwEAYDVQQDEwlzeW5jdGhpbmcwKjAFBgMrZXADIQB0QK+PM7oLutgCKoIo
|
||||
UOh8/XiSmGJWbkN175hALTIaYKNVMFMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQW
|
||||
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
|
||||
CXN5bmN0aGluZzAFBgMrZXADQQAbedm895vhgYizMXc38IwhquYv2S4ORHZac0Bw
|
||||
ZYKJKze7EhKzqvdxcU5uVIUaMPSGi86wtmmsiijfhY8rnD0E
|
||||
-----END CERTIFICATE-----
|
||||
1
nixos/tests/syncthing/test-nodes/c/id
Normal file
1
nixos/tests/syncthing/test-nodes/c/id
Normal file
@@ -0,0 +1 @@
|
||||
MPGAF2L-AUIF5DE-UCI3AMX-PTGF3ZI-XDS6UJI-YUU4ZWT-UUWY7X6-N2DAAQG
|
||||
3
nixos/tests/syncthing/test-nodes/c/key.pem
Normal file
3
nixos/tests/syncthing/test-nodes/c/key.pem
Normal file
@@ -0,0 +1,3 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MC4CAQAwBQYDK2VwBCIEIMSmcIlXQTKkaMaOLh+zsgU1ULCvCz949E56OzQ1dsMK
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -126,6 +126,8 @@ in
|
||||
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||
};
|
||||
|
||||
interactive.sshBackdoor.enable = true;
|
||||
|
||||
testScript =
|
||||
let
|
||||
changePassword = pkgs.writeText "change-password.py" ''
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 = ''
|
||||
|
||||
@@ -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=",
|
||||
|
||||
50
pkgs/by-name/ac/aclpubcheck/package.nix
Normal file
50
pkgs/by-name/ac/aclpubcheck/package.nix
Normal 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 ];
|
||||
};
|
||||
}
|
||||
29
pkgs/by-name/ac/aclpubcheck/test/default.nix
Normal file
29
pkgs/by-name/ac/aclpubcheck/test/default.nix
Normal 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"
|
||||
''
|
||||
28
pkgs/by-name/ac/aclpubcheck/test/expected-output.txt
Normal file
28
pkgs/by-name/ac/aclpubcheck/test/expected-output.txt
Normal 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.
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ];
|
||||
|
||||
|
||||
@@ -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=";
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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=";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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
|
||||
;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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
|
||||
|
||||
161
pkgs/by-name/co/convey/package.nix
Normal file
161
pkgs/by-name/co/convey/package.nix
Normal 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;
|
||||
};
|
||||
})
|
||||
@@ -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
|
||||
];
|
||||
};
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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 ];
|
||||
};
|
||||
})
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 {
|
||||
|
||||
40
pkgs/by-name/en/enroll/package.nix
Normal file
40
pkgs/by-name/en/enroll/package.nix
Normal 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;
|
||||
};
|
||||
})
|
||||
@@ -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;
|
||||
};
|
||||
})
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ];
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -78,5 +78,6 @@ stdenv.mkDerivation rec {
|
||||
];
|
||||
license = lib.licenses.gpl2Only;
|
||||
mainProgram = "kexec";
|
||||
maintainers = [ lib.maintainers.zowoq ];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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");
|
||||
- });
|
||||
-});
|
||||
|
||||
@@ -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"
|
||||
'';
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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().
|
||||
|
||||
@@ -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 ];
|
||||
|
||||
|
||||
@@ -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=";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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";
|
||||
};
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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; [
|
||||
|
||||
@@ -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}");
|
||||
};
|
||||
|
||||
67
pkgs/by-name/wh/whisrs/package.nix
Normal file
67
pkgs/by-name/wh/whisrs/package.nix
Normal 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;
|
||||
};
|
||||
})
|
||||
@@ -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.
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
inherit (import ./cuda.nix { inherit _cuda lib; })
|
||||
_cudaCapabilityIsDefault
|
||||
_cudaCapabilityIsSupported
|
||||
_mkCudaVariant
|
||||
_mkCudaVariants
|
||||
allowUnfreeCudaPredicate
|
||||
;
|
||||
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -146,6 +146,7 @@ let
|
||||
inherit (finalCudaPackages)
|
||||
autoAddCudaCompatRunpath
|
||||
backendStdenv
|
||||
cudaMajorMinorPatchVersion
|
||||
cudaMajorMinorVersion
|
||||
cudaMajorVersion
|
||||
cudaNamePrefix
|
||||
|
||||
@@ -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.";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,5 +13,6 @@ buildRedist {
|
||||
ptx text from host binaries.
|
||||
'';
|
||||
homepage = "https://docs.nvidia.com/cuda/cuda-binary-utilities#cuobjdump";
|
||||
mainProgram = "cuobjdump";
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
};
|
||||
}
|
||||
|
||||
56
pkgs/development/ocaml-modules/capnp-rpc/default.nix
Normal file
56
pkgs/development/ocaml-modules/capnp-rpc/default.nix
Normal 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;
|
||||
};
|
||||
})
|
||||
53
pkgs/development/ocaml-modules/capnp-rpc/net.nix
Normal file
53
pkgs/development/ocaml-modules/capnp-rpc/net.nix
Normal 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
Reference in New Issue
Block a user