Merge staging-next into staging

Changed files
+1889 -1762
maintainers
nixos
pkgs
applications
blockchains
polkadot
misc
qelectrotech
networking
browsers
firefox
science
math
glsurf
video
obs-studio
by-name
ai
aiken
an
ankama-launcher
ba
balena-cli
ca
caddy
cl
co
cosmic-applibrary
ex
exiv2
fs
fsautocomplete
gi
git-big-picture
go
google-chrome
gotosocial
ho
hoppscotch
ku
le
op
open-adventure
pe
pebble
ra
radsecproxy
sn
sy
symfony-cli
up
vt
vtsls
wa
wasm-language-tools
development
libraries
python-qt
ocaml-modules
php-packages
memprof
python-modules
craft-archives
graph-tool
gym
pylance
kde
generated
sources
servers
http
apache-httpd
nginx
x11
tools
audio
liquidsoap
inputmethods
ibus-engines
ibus-libpinyin
misc
top-level
+5
maintainers/maintainer-list.nix
···
githubId = 934284;
name = "Rouven Czerwinski";
};
+
EmanuelM153 = {
+
name = "Emanuel";
+
github = "EmanuelM153";
+
githubId = 134736553;
+
};
emattiza = {
email = "nix@mattiza.dev";
github = "emattiza";
+79 -90
nixos/modules/security/acme/default.nix
···
mkAccountHash = acmeServer: data: mkHash "${toString acmeServer} ${data.keyType} ${data.email}";
accountDirRoot = "/var/lib/acme/.lego/accounts/";
+
# Lockdir is acme-setup.service's RuntimeDirectory.
+
# Since that service is a oneshot with RemainAfterExit,
+
# the folder will exist during all renewal services.
lockdir = "/run/acme/";
concurrencyLockfiles = map (n: "${toString n}.lock") (lib.range 1 cfg.maxConcurrentRenewals);
# Assign elements of `baseList` to each element of `needAssignmentList`, until the latter is exhausted.
···
];
};
-
# In order to avoid race conditions creating the CA for selfsigned certs,
-
# we have a separate service which will create the necessary files.
-
selfsignCAService = {
-
description = "Generate self-signed certificate authority";
-
-
path = with pkgs; [ minica ];
-
-
unitConfig = {
-
ConditionPathExists = "!/var/lib/acme/.minica/key.pem";
-
StartLimitIntervalSec = 0;
-
};
-
-
serviceConfig = commonServiceConfig // {
-
StateDirectory = "acme/.minica";
-
BindPaths = "/var/lib/acme/.minica:/tmp/ca";
-
UMask = "0077";
-
};
-
-
# Working directory will be /tmp
-
script = ''
-
minica \
-
--ca-key ca/key.pem \
-
--ca-cert ca/cert.pem \
-
--domains selfsigned.local
-
'';
-
};
-
# Ensures that directories which are shared across all certs
# exist and have the correct user and group, since group
# is configurable on a per-cert basis.
-
userMigrationService = let
-
script = with builtins; ''
+
# writeShellScriptBin is used as it produces a nicer binary name, which
+
# journalctl will show when the service is running.
+
privilegedSetupScript = pkgs.writeShellScriptBin "acme-setup-privileged" (
+
''
+
${lib.optionalString cfg.defaults.enableDebugLogs "set -x"}
+
set -euo pipefail
+
cd /var/lib/acme
+
chmod -R u=rwX,g=,o= .lego/accounts
chown -R ${user} .lego/accounts
'' + (lib.concatStringsSep "\n" (lib.mapAttrsToList (cert: data: ''
for fixpath in ${lib.escapeShellArg cert} .lego/${lib.escapeShellArg cert}; do
···
chown -R ${user}:${data.group} "$fixpath"
fi
done
-
'') certConfigs));
-
in {
-
description = "Fix owner and group of all ACME certificates";
+
'') certConfigs))
+
);
-
serviceConfig = commonServiceConfig // {
-
# We don't want this to run every time a renewal happens
-
RemainAfterExit = true;
+
# This is defined with lib.mkMerge so that we can separate the config per function.
+
setupService = lib.mkMerge [
+
{
+
description = "Set up the ACME certificate renewal infrastructure";
+
script = lib.mkBefore ''
+
${lib.optionalString cfg.defaults.enableDebugLogs "set -x"}
+
set -euo pipefail
+
'';
+
serviceConfig = commonServiceConfig // {
+
# This script runs with elevated privileges, denoted by the +
+
# ExecStartPre is used instead of ExecStart so that the `script` continues to work.
+
ExecStartPre = "+${lib.getExe privilegedSetupScript}";
-
# StateDirectory entries are a cleaner, service-level mechanism
-
# for dealing with persistent service data
-
StateDirectory = [ "acme" "acme/.lego" "acme/.lego/accounts" ];
-
StateDirectoryMode = 755;
-
WorkingDirectory = "/var/lib/acme";
+
# We don't want this to run every time a renewal happens
+
RemainAfterExit = true;
-
# Run the start script as root
-
ExecStart = "+" + (pkgs.writeShellScript "acme-fixperms" script);
-
};
-
};
-
lockfilePrepareService = {
-
description = "Manage lock files for acme services";
+
# StateDirectory entries are a cleaner, service-level mechanism
+
# for dealing with persistent service data
+
StateDirectory = [ "acme" "acme/.lego" "acme/.lego/accounts" ];
+
StateDirectoryMode = "0755";
-
# ensure all required lock files exist, but none more
-
script = ''
-
GLOBIGNORE="${lib.concatStringsSep ":" concurrencyLockfiles}"
-
rm -f -- *
-
unset GLOBIGNORE
+
# Creates ${lockdir}. Earlier RemainAfterExit=true means
+
# it does not get deleted immediately.
+
RuntimeDirectory = "acme";
+
RuntimeDirectoryMode = "0700";
-
xargs touch <<< "${toString concurrencyLockfiles}"
-
'';
-
-
serviceConfig = commonServiceConfig // {
-
# We don't want this to run every time a renewal happens
-
RemainAfterExit = true;
-
WorkingDirectory = lockdir;
-
};
-
};
+
# Generally, we don't write anything that should be group accessible.
+
# Group varies for most ACME units, and setup files are only used
+
# under the acme user.
+
UMask = "0077";
+
};
+
}
+
# Avoid race conditions creating the CA for selfsigned certs
+
(lib.mkIf cfg.preliminarySelfsigned {
+
path = [ pkgs.minica ];
+
# Working directory will be /tmp
+
script = ''
+
test -e ca/key.pem || minica \
+
--ca-key ca/key.pem \
+
--ca-cert ca/cert.pem \
+
--domains selfsigned.local
+
'';
+
serviceConfig = {
+
StateDirectory = [ "acme/.minica" ];
+
BindPaths = "/var/lib/acme/.minica:/tmp/ca";
+
};
+
})
+
];
certToConfig = cert: data: let
acmeServer = data.server;
···
selfsignService = lockfileName: {
description = "Generate self-signed certificate for ${cert}";
-
after = [ "acme-selfsigned-ca.service" "acme-fixperms.service" ] ++ lib.optional (cfg.maxConcurrentRenewals > 0) "acme-lockfiles.service";
-
requires = [ "acme-selfsigned-ca.service" "acme-fixperms.service" ] ++ lib.optional (cfg.maxConcurrentRenewals > 0) "acme-lockfiles.service";
+
after = [ "acme-setup.service" ];
+
requires = [ "acme-setup.service" ];
-
path = with pkgs; [ minica ];
+
path = [ pkgs.minica ];
unitConfig = {
ConditionPathExists = "!/var/lib/acme/${cert}/key.pem";
···
renewService = lockfileName: {
description = "Renew ACME certificate for ${cert}";
-
after = [ "network.target" "network-online.target" "acme-fixperms.service" "nss-lookup.target" ] ++ selfsignedDeps ++ lib.optional (cfg.maxConcurrentRenewals > 0) "acme-lockfiles.service";
-
wants = [ "network-online.target" "acme-fixperms.service" ] ++ selfsignedDeps ++ lib.optional (cfg.maxConcurrentRenewals > 0) "acme-lockfiles.service";
+
after = [ "network.target" "network-online.target" "acme-setup.service" "nss-lookup.target" ] ++ selfsignedDeps;
+
wants = [ "network-online.target" ] ++ selfsignedDeps;
+
requires = [ "acme-setup.service" ];
# https://github.com/NixOS/nixpkgs/pull/81371#issuecomment-605526099
wantedBy = lib.optionals (!config.boot.isContainer) [ "multi-user.target" ];
···
# By default group will have no access to the cert files.
# This chmod will fix that.
chmod 640 out/*
+
+
# Also ensure safer permissions on the account directory.
+
chmod -R u=rwX,g=,o= accounts/.
'';
};
};
···
'';
};
+
listenHTTP = lib.mkOption {
+
type = lib.types.nullOr lib.types.str;
+
inherit (defaultAndText "listenHTTP" null) default defaultText;
+
example = ":1360";
+
description = ''
+
Interface and port to listen on to solve HTTP challenges
+
in the form `[INTERFACE]:PORT`.
+
If you use a port other than 80, you must proxy port 80 to this port.
+
'';
+
};
+
dnsProvider = lib.mkOption {
type = lib.types.nullOr lib.types.str;
inherit (defaultAndText "dnsProvider" null) default defaultText;
···
'';
};
-
# This setting must be different for each configured certificate, otherwise
-
# two or more renewals may fail to bind to the address. Hence, it is not in
-
# the inheritableOpts.
-
listenHTTP = lib.mkOption {
-
type = lib.types.nullOr lib.types.str;
-
default = null;
-
example = ":1360";
-
description = ''
-
Interface and port to listen on to solve HTTP challenges
-
in the form [INTERFACE]:PORT.
-
If you use a port other than 80, you must proxy port 80 to this port.
-
'';
-
};
-
s3Bucket = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
···
users.groups.acme = {};
-
# for lock files, still use tmpfiles as they should better reside in /run
-
systemd.tmpfiles.rules = [
-
"d ${lockdir} 0700 ${user} - - -"
-
"Z ${lockdir} 0700 ${user} - - -"
-
];
-
systemd.services = let
renewServiceFunctions = lib.mapAttrs' (cert: conf: lib.nameValuePair "acme-${cert}" conf.renewService) certConfigs;
renewServices = if cfg.maxConcurrentRenewals > 0
···
then roundRobinApplyAttrs selfsignServiceFunctions concurrencyLockfiles
else lib.mapAttrs (_: f: f null) selfsignServiceFunctions;
in
-
{ "acme-fixperms" = userMigrationService; }
-
// (lib.optionalAttrs (cfg.maxConcurrentRenewals > 0) {"acme-lockfiles" = lockfilePrepareService; })
+
{ acme-setup = setupService; }
// renewServices
-
// (lib.optionalAttrs (cfg.preliminarySelfsigned) ({
-
"acme-selfsigned-ca" = selfsignCAService;
-
} // selfsignServices));
+
// lib.optionalAttrs cfg.preliminarySelfsigned selfsignServices;
systemd.timers = lib.mapAttrs' (cert: conf: lib.nameValuePair "acme-${cert}" conf.renewTimer) certConfigs;
+46
nixos/modules/services/web-servers/h2o/common.nix
···
+
{ lib }:
+
{
+
tlsRecommendationsOption = lib.mkOption {
+
type = lib.types.nullOr (
+
lib.types.enum [
+
"modern"
+
"intermediate"
+
"old"
+
]
+
);
+
default = null;
+
example = "intermediate";
+
description = ''
+
By default, H2O, without prejudice, will use as many TLS versions &
+
cipher suites as it & the TLS library (OpenSSL) can support. The user is
+
expected to hone settings for the security of their server. Setting some
+
constraints is recommended, & if unsure about what TLS settings to use,
+
this option gives curated TLS settings recommendations from Mozilla’s
+
‘SSL Configuration Generator’ project (see
+
<https://ssl-config.mozilla.org>) or read more at Mozilla’s Wiki (see
+
<https://wiki.mozilla.org/Security/Server_Side_TLS>).
+
+
modern
+
: Services with clients that support TLS 1.3 & don’t need backward
+
compatibility
+
+
intermediate
+
: General-purpose servers with a variety of clients, recommended for
+
almost all systems
+
+
old
+
: Compatible with a number of very old clients, & should be used only as
+
a last resort
+
+
The default for all virtual hosts can be set with
+
services.h2o.defaultTLSRecommendations, but this value can be overridden
+
on a per-host basis using services.h2o.hosts.<name>.tls.recommmendations.
+
The settings will also be overidden by manual values set with
+
services.settings.h2o.hosts.<name>.tls.extraSettings.
+
+
NOTE: older/weaker ciphers might require overriding the OpenSSL version
+
of H2O (such as `openssl_legacy`). This can be done with
+
sevices.settings.h2o.package.
+
'';
+
};
+
}
+111 -28
nixos/modules/services/web-servers/h2o/default.nix
···
}:
# TODO: Gems includes for Mruby
-
# TODO: Recommended options
let
cfg = config.services.h2o;
inherit (config.security.acme) certs;
···
;
mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix lib;
+
+
inherit (import ./common.nix { inherit lib; }) tlsRecommendationsOption;
settingsFormat = pkgs.formats.yaml { };
···
all = certNames'.dependent ++ certNames'.independent;
};
+
mozTLSRecs =
+
if cfg.defaultTLSRecommendations != null then
+
let
+
# NOTE: if updating, *do* verify the changes then adjust ciphers &
+
# other settings with the tests @
+
# `nixos/tests/web-servers/h2o/tls-recommendations.nix`
+
# & run with `nix-build -A nixosTests.h2o.tls-recommendations`
+
version = "5.7";
+
git_tag = "v5.7.1";
+
guidelinesJSON =
+
lib.pipe
+
{
+
urls = [
+
"https://ssl-config.mozilla.org/guidelines/${version}.json"
+
"https://raw.githubusercontent.com/mozilla/ssl-config-generator/refs/tags/${git_tag}/src/static/guidelines/${version}.json"
+
];
+
sha256 = "sha256:1mj2pcb1hg7q2wpgdq3ac8pc2q64wvwvwlkb9xjmdd9jm4hiyny7";
+
}
+
[
+
pkgs.fetchurl
+
builtins.readFile
+
builtins.fromJSON
+
];
+
in
+
guidelinesJSON.configurations
+
else
+
null;
+
hostsConfig = lib.concatMapAttrs (
name: value:
let
···
]
)
{
-
"${names.server}:${builtins.toString port.TLS}" = value.settings // {
-
listen =
-
let
-
identity =
-
value.tls.identity
-
++ lib.optional (builtins.elem names.cert certNames.all) {
-
key-file = "${certs.${names.cert}.directory}/key.pem";
-
certificate-file = "${certs.${names.cert}.directory}/fullchain.pem";
+
"${names.server}:${builtins.toString port.TLS}" =
+
let
+
tlsRecommendations = lib.attrByPath [ "tls" "recommendations" ] cfg.defaultTLSRecommendations value;
+
+
hasTLSRecommendations = tlsRecommendations != null && mozTLSRecs != null;
+
+
# NOTE: Let’s Encrypt has sunset OCSP stapling. Mozilla’s
+
# ssl-config-generator is at present still recommending this setting, but
+
# this module will skip setting a stapling value as Let’s Encrypt +
+
# ACME is the most likely use case.
+
#
+
# See: https://github.com/mozilla/ssl-config-generator/issues/323
+
tlsRecAttrs = lib.optionalAttrs hasTLSRecommendations (
+
let
+
recs = mozTLSRecs.${tlsRecommendations};
+
in
+
{
+
min-version = builtins.head recs.tls_versions;
+
cipher-preference = "server";
+
"cipher-suite-tls1.3" = recs.ciphersuites;
+
}
+
// lib.optionalAttrs (recs.ciphers.openssl != [ ]) {
+
cipher-suite = lib.concatStringsSep ":" recs.ciphers.openssl;
+
}
+
);
+
+
headerRecAttrs =
+
lib.optionalAttrs
+
(
+
hasTLSRecommendations
+
&& value.tls != null
+
&& builtins.elem value.tls.policy [
+
"force"
+
"only"
+
]
+
)
+
(
+
let
+
headerSet = value.settings."header.set" or [ ];
+
recs = mozTLSRecs.${tlsRecommendations};
+
hsts = "Strict-Transport-Security: max-age=${builtins.toString recs.hsts_min_age}; includeSubDomains; preload";
+
in
+
{
+
"header.set" =
+
if builtins.isString headerSet then
+
[
+
headerSet
+
hsts
+
]
+
else
+
headerSet ++ [ hsts ];
+
}
+
);
+
in
+
value.settings
+
// headerRecAttrs
+
// {
+
listen =
+
let
+
identity =
+
value.tls.identity
+
++ lib.optional (builtins.elem names.cert certNames.all) {
+
key-file = "${certs.${names.cert}.directory}/key.pem";
+
certificate-file = "${certs.${names.cert}.directory}/fullchain.pem";
+
};
+
in
+
{
+
port = port.TLS;
+
ssl = (lib.recursiveUpdate tlsRecAttrs value.tls.extraSettings) // {
+
inherit identity;
};
-
in
-
{
-
port = port.TLS;
-
ssl = value.tls.extraSettings // {
-
inherit identity;
};
-
};
-
};
+
};
};
in
# With a high likelihood of HTTP & ACME challenges being on the same port,
···
};
package = lib.mkPackageOption pkgs "h2o" {
-
example = ''
-
pkgs.h2o.override {
-
withMruby = false;
-
};
-
'';
+
example = # nix
+
''
+
pkgs.h2o.override {
+
withMruby = false;
+
openssl = pkgs.openssl_legacy;
+
}
+
'';
};
defaultHTTPListenPort = mkOption {
···
example = 8443;
};
+
defaultTLSRecommendations = tlsRecommendationsOption;
+
settings = mkOption {
type = settingsFormat.type;
default = { };
···
};
hosts = mkOption {
-
type = types.attrsOf (
-
types.submodule (
-
import ./vhost-options.nix {
-
inherit config lib;
-
}
-
)
-
);
+
type = types.attrsOf (types.submodule (import ./vhost-options.nix { inherit config lib; }));
default = { };
description = ''
The `hosts` config to be merged with the settings.
+9 -1
nixos/modules/services/web-servers/h2o/vhost-options.nix
···
-
{ config, lib, ... }:
+
{
+
config,
+
lib,
+
...
+
}:
let
inherit (lib)
···
mkOption
types
;
+
+
inherit (import ./common.nix { inherit lib; }) tlsRecommendationsOption;
in
{
options = {
···
]
'';
};
+
recommendations = tlsRecommendationsOption;
extraSettings = mkOption {
type = types.attrs;
default = { };
···
settings = mkOption {
type = types.attrs;
+
default = { };
description = ''
Attrset to be transformed into YAML for host config. Note that the HTTP
/ TLS configurations will override these config values.
+2 -1
nixos/release-combined.nix
···
(onFullSupported "nixos.iso_gnome")
(onFullSupported "nixos.manual")
(onSystems [ "aarch64-linux" ] "nixos.sd_image")
-
(onFullSupported "nixos.tests.acme")
+
(onFullSupported "nixos.tests.acme.http01-builtin")
+
(onFullSupported "nixos.tests.acme.dns01")
(onSystems [ "x86_64-linux" ] "nixos.tests.boot.biosCdrom")
(onSystems [ "x86_64-linux" ] "nixos.tests.boot.biosUsb")
(onFullSupported "nixos.tests.boot-stage1")
+8 -2
nixos/release-small.nix
···
dummy
;
tests = {
+
acme = {
+
inherit (nixos'.tests.acme)
+
http01-builtin
+
dns01
+
;
+
};
inherit (nixos'.tests)
-
acme
containers-imperative
containers-ip
firewall
···
(map onSupported [
"nixos.dummy"
"nixos.manual"
-
"nixos.tests.acme"
+
"nixos.tests.acme.http01-builtin"
+
"nixos.tests.acme.dns01"
"nixos.tests.containers-imperative"
"nixos.tests.containers-ip"
"nixos.tests.firewall"
-788
nixos/tests/acme.nix
···
-
{ config, lib, ... }: let
-
-
pkgs = config.node.pkgs;
-
-
commonConfig = ./common/acme/client;
-
-
dnsServerIP = nodes: nodes.dnsserver.networking.primaryIPAddress;
-
-
dnsScript = nodes: let
-
dnsAddress = dnsServerIP nodes;
-
in pkgs.writeShellScript "dns-hook.sh" ''
-
set -euo pipefail
-
echo '[INFO]' "[$2]" 'dns-hook.sh' $*
-
if [ "$1" = "present" ]; then
-
${pkgs.curl}/bin/curl --data '{"host": "'"$2"'", "value": "'"$3"'"}' http://${dnsAddress}:8055/set-txt
-
else
-
${pkgs.curl}/bin/curl --data '{"host": "'"$2"'"}' http://${dnsAddress}:8055/clear-txt
-
fi
-
'';
-
-
dnsConfig = nodes: {
-
dnsProvider = "exec";
-
dnsPropagationCheck = false;
-
environmentFile = pkgs.writeText "wildcard.env" ''
-
EXEC_PATH=${dnsScript nodes}
-
EXEC_POLLING_INTERVAL=1
-
EXEC_PROPAGATION_TIMEOUT=1
-
EXEC_SEQUENCE_INTERVAL=1
-
'';
-
};
-
-
documentRoot = pkgs.runCommand "docroot" {} ''
-
mkdir -p "$out"
-
echo hello world > "$out/index.html"
-
'';
-
-
vhostBase = {
-
forceSSL = true;
-
locations."/".root = documentRoot;
-
};
-
-
vhostBaseHttpd = {
-
forceSSL = true;
-
inherit documentRoot;
-
};
-
-
simpleConfig = {
-
security.acme = {
-
certs."http.example.test" = {
-
listenHTTP = ":80";
-
};
-
};
-
-
networking.firewall.allowedTCPPorts = [ 80 ];
-
};
-
-
# Base specialisation config for testing general ACME features
-
webserverBasicConfig = {
-
services.nginx.enable = true;
-
services.nginx.virtualHosts."a.example.test" = vhostBase // {
-
enableACME = true;
-
};
-
};
-
-
# Generate specialisations for testing a web server
-
mkServerConfigs = { server, group, vhostBaseData, extraConfig ? {} }: let
-
baseConfig = { nodes, config, specialConfig ? {} }: lib.mkMerge [
-
{
-
security.acme = {
-
defaults = (dnsConfig nodes);
-
# One manual wildcard cert
-
certs."example.test" = {
-
domain = "*.example.test";
-
};
-
};
-
-
users.users."${config.services."${server}".user}".extraGroups = ["acme"];
-
-
services."${server}" = {
-
enable = true;
-
virtualHosts = {
-
# Run-of-the-mill vhost using HTTP-01 validation
-
"${server}-http.example.test" = vhostBaseData // {
-
serverAliases = [ "${server}-http-alias.example.test" ];
-
enableACME = true;
-
};
-
-
# Another which inherits the DNS-01 config
-
"${server}-dns.example.test" = vhostBaseData // {
-
serverAliases = [ "${server}-dns-alias.example.test" ];
-
enableACME = true;
-
# Set acmeRoot to null instead of using the default of "/var/lib/acme/acme-challenge"
-
# webroot + dnsProvider are mutually exclusive.
-
acmeRoot = null;
-
};
-
-
# One using the wildcard certificate
-
"${server}-wildcard.example.test" = vhostBaseData // {
-
serverAliases = [ "${server}-wildcard-alias.example.test" ];
-
useACMEHost = "example.test";
-
};
-
} // (lib.optionalAttrs (server == "nginx") {
-
# The nginx module supports using a different key than the hostname
-
different-key = vhostBaseData // {
-
serverName = "${server}-different-key.example.test";
-
serverAliases = [ "${server}-different-key-alias.example.test" ];
-
enableACME = true;
-
};
-
});
-
};
-
-
# Used to determine if service reload was triggered
-
systemd.targets."test-renew-${server}" = {
-
wants = [ "acme-${server}-http.example.test.service" ];
-
after = [ "acme-${server}-http.example.test.service" "${server}-config-reload.service" ];
-
};
-
}
-
specialConfig
-
extraConfig
-
];
-
in {
-
"${server}".configuration = { nodes, config, ... }: baseConfig {
-
inherit nodes config;
-
};
-
-
# Test that server reloads when an alias is removed (and subsequently test removal works in acme)
-
"${server}_remove_alias".configuration = { nodes, config, ... }: baseConfig {
-
inherit nodes config;
-
specialConfig = {
-
# Remove an alias, but create a standalone vhost in its place for testing.
-
# This configuration results in certificate errors as useACMEHost does not imply
-
# append extraDomains, and thus we can validate the SAN is removed.
-
services."${server}" = {
-
virtualHosts."${server}-http.example.test".serverAliases = lib.mkForce [];
-
virtualHosts."${server}-http-alias.example.test" = vhostBaseData // {
-
useACMEHost = "${server}-http.example.test";
-
};
-
};
-
};
-
};
-
-
# Test that the server reloads when only the acme configuration is changed.
-
"${server}_change_acme_conf".configuration = { nodes, config, ... }: baseConfig {
-
inherit nodes config;
-
specialConfig = {
-
security.acme.certs."${server}-http.example.test" = {
-
keyType = "ec384";
-
# Also test that postRun is exec'd as root
-
postRun = "id | grep root";
-
};
-
};
-
};
-
};
-
-
in {
-
name = "acme";
-
meta = {
-
maintainers = lib.teams.acme.members;
-
# Hard timeout in seconds. Average run time is about 7 minutes.
-
timeout = 1800;
-
};
-
-
nodes = {
-
# The fake ACME server which will respond to client requests
-
acme = { nodes, ... }: {
-
imports = [ ./common/acme/server ];
-
networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ];
-
};
-
-
# A fake DNS server which can be configured with records as desired
-
# Used to test DNS-01 challenge
-
dnsserver = { nodes, ... }: {
-
networking.firewall.allowedTCPPorts = [ 8055 53 ];
-
networking.firewall.allowedUDPPorts = [ 53 ];
-
systemd.services.pebble-challtestsrv = {
-
enable = true;
-
description = "Pebble ACME challenge test server";
-
wantedBy = [ "network.target" ];
-
serviceConfig = {
-
ExecStart = "${pkgs.pebble}/bin/pebble-challtestsrv -dns01 ':53' -defaultIPv6 '' -defaultIPv4 '${nodes.webserver.networking.primaryIPAddress}'";
-
# Required to bind on privileged ports.
-
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
-
};
-
};
-
};
-
-
# A web server which will be the node requesting certs
-
webserver = { nodes, config, ... }: {
-
imports = [ commonConfig ];
-
networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ];
-
networking.firewall.allowedTCPPorts = [ 80 443 ];
-
-
# OpenSSL will be used for more thorough certificate validation
-
environment.systemPackages = [ pkgs.openssl ];
-
-
# Set log level to info so that we can see when the service is reloaded
-
services.nginx.logError = "stderr info";
-
-
specialisation = {
-
# Tests HTTP-01 verification using Lego's built-in web server
-
http01lego.configuration = simpleConfig;
-
-
# account hash generation with default server from <= 23.11
-
http01lego_legacyAccountHash.configuration = lib.mkMerge [
-
simpleConfig
-
{
-
security.acme.defaults.server = lib.mkForce null;
-
}
-
];
-
-
renew.configuration = lib.mkMerge [
-
simpleConfig
-
{
-
# Pebble provides 5 year long certs,
-
# needs to be higher than that to test renewal
-
security.acme.certs."http.example.test".validMinDays = 9999;
-
}
-
];
-
-
# Tests that account creds can be safely changed.
-
accountchange.configuration = lib.mkMerge [
-
simpleConfig
-
{
-
security.acme.certs."http.example.test".email = "admin@example.test";
-
}
-
];
-
-
# First derivation used to test general ACME features
-
general.configuration = { ... }: let
-
caDomain = nodes.acme.test-support.acme.caDomain;
-
email = config.security.acme.defaults.email;
-
# Exit 99 to make it easier to track if this is the reason a renew failed
-
accountCreateTester = ''
-
test -e accounts/${caDomain}/${email}/account.json || exit 99
-
'';
-
in lib.mkMerge [
-
webserverBasicConfig
-
{
-
# Used to test that account creation is collated into one service.
-
# These should not run until after acme-finished-a.example.test.target
-
systemd.services."b.example.test".preStart = accountCreateTester;
-
systemd.services."c.example.test".preStart = accountCreateTester;
-
-
services.nginx.virtualHosts."b.example.test" = vhostBase // {
-
enableACME = true;
-
};
-
services.nginx.virtualHosts."c.example.test" = vhostBase // {
-
enableACME = true;
-
};
-
}
-
];
-
-
# Test OCSP Stapling
-
ocsp_stapling.configuration = { ... }: lib.mkMerge [
-
webserverBasicConfig
-
{
-
security.acme.certs."a.example.test".ocspMustStaple = true;
-
services.nginx.virtualHosts."a.example.test" = {
-
extraConfig = ''
-
ssl_stapling on;
-
ssl_stapling_verify on;
-
'';
-
};
-
}
-
];
-
-
# Validate service relationships by adding a slow start service to nginx' wants.
-
# Reproducer for https://github.com/NixOS/nixpkgs/issues/81842
-
slow_startup.configuration = { ... }: lib.mkMerge [
-
webserverBasicConfig
-
{
-
systemd.services.my-slow-service = {
-
wantedBy = [ "multi-user.target" "nginx.service" ];
-
before = [ "nginx.service" ];
-
preStart = "sleep 5";
-
script = "${pkgs.python3}/bin/python -m http.server";
-
};
-
-
services.nginx.virtualHosts."slow.example.test" = {
-
forceSSL = true;
-
enableACME = true;
-
locations."/".proxyPass = "http://localhost:8000";
-
};
-
}
-
];
-
-
concurrency_limit.configuration = {pkgs, ...}: lib.mkMerge [
-
webserverBasicConfig {
-
security.acme.maxConcurrentRenewals = 1;
-
-
services.nginx.virtualHosts = {
-
"f.example.test" = vhostBase // {
-
enableACME = true;
-
};
-
"g.example.test" = vhostBase // {
-
enableACME = true;
-
};
-
"h.example.test" = vhostBase // {
-
enableACME = true;
-
};
-
};
-
-
systemd.services = {
-
# check for mutual exclusion of starting renew services
-
"acme-f.example.test".serviceConfig.ExecPreStart = "+" + (pkgs.writeShellScript "test-f" ''
-
test "$(systemctl is-active acme-{g,h}.example.test.service | grep activating | wc -l)" -le 0
-
'');
-
"acme-g.example.test".serviceConfig.ExecPreStart = "+" + (pkgs.writeShellScript "test-g" ''
-
test "$(systemctl is-active acme-{f,h}.example.test.service | grep activating | wc -l)" -le 0
-
'');
-
"acme-h.example.test".serviceConfig.ExecPreStart = "+" + (pkgs.writeShellScript "test-h" ''
-
test "$(systemctl is-active acme-{g,f}.example.test.service | grep activating | wc -l)" -le 0
-
'');
-
};
-
}
-
];
-
-
# Test lego internal server (listenHTTP option)
-
# Also tests useRoot option
-
lego_server.configuration = { ... }: {
-
security.acme.useRoot = true;
-
security.acme.certs."lego.example.test" = {
-
listenHTTP = ":80";
-
group = "nginx";
-
};
-
services.nginx.enable = true;
-
services.nginx.virtualHosts."lego.example.test" = {
-
useACMEHost = "lego.example.test";
-
onlySSL = true;
-
};
-
};
-
-
# Test compatibility with Caddy
-
# It only supports useACMEHost, hence not using mkServerConfigs
-
} // (let
-
baseCaddyConfig = { nodes, config, ... }: {
-
security.acme = {
-
defaults = (dnsConfig nodes);
-
# One manual wildcard cert
-
certs."example.test" = {
-
domain = "*.example.test";
-
};
-
};
-
-
users.users."${config.services.caddy.user}".extraGroups = ["acme"];
-
-
services.caddy = {
-
enable = true;
-
virtualHosts."a.example.test" = {
-
useACMEHost = "example.test";
-
extraConfig = ''
-
root * ${documentRoot}
-
'';
-
};
-
};
-
};
-
in {
-
caddy.configuration = baseCaddyConfig;
-
-
# Test that the server reloads when only the acme configuration is changed.
-
"caddy_change_acme_conf".configuration = { nodes, config, ... }: lib.mkMerge [
-
(baseCaddyConfig {
-
inherit nodes config;
-
})
-
{
-
security.acme.certs."example.test" = {
-
keyType = "ec384";
-
};
-
}
-
];
-
-
# Test compatibility with Nginx
-
}) // (mkServerConfigs {
-
server = "nginx";
-
group = "nginx";
-
vhostBaseData = vhostBase;
-
})
-
-
# Test compatibility with Apache HTTPD
-
// (mkServerConfigs {
-
server = "httpd";
-
group = "wwwrun";
-
vhostBaseData = vhostBaseHttpd;
-
extraConfig = {
-
services.httpd.adminAddr = config.security.acme.defaults.email;
-
};
-
});
-
};
-
-
# The client will be used to curl the webserver to validate configuration
-
client = { nodes, ... }: {
-
imports = [ commonConfig ];
-
networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ];
-
-
# OpenSSL will be used for more thorough certificate validation
-
environment.systemPackages = [ pkgs.openssl ];
-
};
-
};
-
-
testScript = { nodes, ... }:
-
let
-
caDomain = nodes.acme.test-support.acme.caDomain;
-
in
-
# Note, wait_for_unit does not work for oneshot services that do not have RemainAfterExit=true,
-
# this is because a oneshot goes from inactive => activating => inactive, and never
-
# reaches the active state. Targets do not have this issue.
-
''
-
import time
-
-
TOTAL_RETRIES = 20
-
-
-
class BackoffTracker(object):
-
delay = 1
-
increment = 1
-
-
def handle_fail(self, retries, message) -> int:
-
assert retries < TOTAL_RETRIES, message
-
-
print(f"Retrying in {self.delay}s, {retries + 1}/{TOTAL_RETRIES}")
-
time.sleep(self.delay)
-
-
# Only increment after the first try
-
if retries == 0:
-
self.delay += self.increment
-
self.increment *= 2
-
-
return retries + 1
-
-
def protect(self, func):
-
def wrapper(*args, retries: int = 0, **kwargs):
-
try:
-
return func(*args, **kwargs)
-
except Exception as err:
-
retries = self.handle_fail(retries, err.args)
-
return wrapper(*args, retries=retries, **kwargs)
-
-
return wrapper
-
-
-
backoff = BackoffTracker()
-
-
-
def switch_to(node, name, allow_fail=False):
-
# On first switch, this will create a symlink to the current system so that we can
-
# quickly switch between derivations
-
root_specs = "/tmp/specialisation"
-
node.execute(
-
f"test -e {root_specs}"
-
f" || ln -s $(readlink /run/current-system)/specialisation {root_specs}"
-
)
-
-
switcher_path = (
-
f"/run/current-system/specialisation/{name}/bin/switch-to-configuration"
-
)
-
rc, _ = node.execute(f"test -e '{switcher_path}'")
-
if rc > 0:
-
switcher_path = f"/tmp/specialisation/{name}/bin/switch-to-configuration"
-
-
if not allow_fail:
-
node.succeed(
-
f"{switcher_path} test"
-
)
-
else:
-
node.execute(
-
f"{switcher_path} test"
-
)
-
-
# Start a unit explicitly, then wait for it to activate.
-
# This is used for the acme-finished-* targets, as those
-
# aren't started by switch-to-configuration, meaning
-
# wait_for_unit(target) will fail with "no pending jobs"
-
# if it wins the race and checks the target state before
-
# the actual unit is started.
-
def start_and_wait(node, unit):
-
node.start_job(unit)
-
node.wait_for_unit(unit)
-
-
# Ensures the issuer of our cert matches the chain
-
# and matches the issuer we expect it to be.
-
# It's a good validation to ensure the cert.pem and fullchain.pem
-
# are not still selfsigned after verification
-
def check_issuer(node, cert_name, issuer):
-
for fname in ("cert.pem", "fullchain.pem"):
-
actual_issuer = node.succeed(
-
f"openssl x509 -noout -issuer -in /var/lib/acme/{cert_name}/{fname}"
-
).partition("=")[2]
-
assert (
-
issuer.lower() in actual_issuer.lower()
-
), f"{fname} issuer mismatch. Expected {issuer} got {actual_issuer}"
-
-
-
# Ensure cert comes before chain in fullchain.pem
-
def check_fullchain(node, cert_name):
-
cert_file = f"/var/lib/acme/{cert_name}/fullchain.pem"
-
num_certs = node.succeed(f"grep -o 'END CERTIFICATE' {cert_file}")
-
assert len(num_certs.strip().split("\n")) > 1, "Insufficient certs in fullchain.pem"
-
-
first_cert_data = node.succeed(
-
f"grep -m1 -B50 'END CERTIFICATE' {cert_file}"
-
" | openssl x509 -noout -text"
-
)
-
for line in first_cert_data.lower().split("\n"):
-
if "dns:" in line:
-
print(f"First DNSName in fullchain.pem: {line}")
-
assert cert_name.lower() in line, f"{cert_name} not found in {line}"
-
return
-
-
assert False
-
-
-
@backoff.protect
-
def check_connection(node, domain):
-
result = node.succeed(
-
"openssl s_client -brief -verify 2 -CAfile /tmp/ca.crt"
-
f" -servername {domain} -connect {domain}:443 < /dev/null 2>&1"
-
)
-
-
for line in result.lower().split("\n"):
-
assert not (
-
"verification" in line and "error" in line
-
), f"Failed to connect to https://{domain}"
-
-
-
@backoff.protect
-
def check_connection_key_bits(node, domain, bits):
-
result = node.succeed(
-
"openssl s_client -CAfile /tmp/ca.crt"
-
f" -servername {domain} -connect {domain}:443 < /dev/null"
-
" | openssl x509 -noout -text | grep -i Public-Key"
-
)
-
print("Key type:", result)
-
-
assert bits in result, f"Did not find expected number of bits ({bits}) in key"
-
-
-
@backoff.protect
-
def check_stapling(node, domain):
-
# Pebble doesn't provide a full OCSP responder, so just check the URL
-
result = node.succeed(
-
"openssl s_client -CAfile /tmp/ca.crt"
-
f" -servername {domain} -connect {domain}:443 < /dev/null"
-
" | openssl x509 -noout -ocsp_uri"
-
)
-
print("OCSP Responder URL:", result)
-
-
assert "${caDomain}:4002" in result.lower(), "OCSP Stapling check failed"
-
-
-
@backoff.protect
-
def download_ca_certs(node):
-
node.succeed("curl https://${caDomain}:15000/roots/0 > /tmp/ca.crt")
-
node.succeed("curl https://${caDomain}:15000/intermediate-keys/0 >> /tmp/ca.crt")
-
-
-
@backoff.protect
-
def set_a_record(node):
-
node.succeed(
-
'curl --data \'{"host": "${caDomain}", "addresses": ["${nodes.acme.networking.primaryIPAddress}"]}\' http://${dnsServerIP nodes}:8055/add-a'
-
)
-
-
-
start_all()
-
-
dnsserver.wait_for_unit("pebble-challtestsrv.service")
-
client.wait_for_unit("default.target")
-
-
set_a_record(client)
-
-
acme.systemctl("start network-online.target")
-
acme.wait_for_unit("network-online.target")
-
acme.wait_for_unit("pebble.service")
-
-
download_ca_certs(client)
-
-
# Perform http-01 w/ lego test first
-
with subtest("Can request certificate with Lego's built in web server"):
-
switch_to(webserver, "http01lego")
-
start_and_wait(webserver, "acme-finished-http.example.test.target")
-
check_fullchain(webserver, "http.example.test")
-
check_issuer(webserver, "http.example.test", "pebble")
-
-
# Perform account hash test
-
with subtest("Assert that account hash didn't unexpectedly change"):
-
hash = webserver.succeed("ls /var/lib/acme/.lego/accounts/")
-
print("Account hash: " + hash)
-
assert hash.strip() == "d590213ed52603e9128d"
-
-
# Perform renewal test
-
with subtest("Can renew certificates when they expire"):
-
hash = webserver.succeed("sha256sum /var/lib/acme/http.example.test/cert.pem")
-
switch_to(webserver, "renew")
-
start_and_wait(webserver, "acme-finished-http.example.test.target")
-
check_fullchain(webserver, "http.example.test")
-
check_issuer(webserver, "http.example.test", "pebble")
-
hash_after = webserver.succeed("sha256sum /var/lib/acme/http.example.test/cert.pem")
-
assert hash != hash_after
-
-
# Perform account change test
-
with subtest("Handles email change correctly"):
-
hash = webserver.succeed("sha256sum /var/lib/acme/http.example.test/cert.pem")
-
switch_to(webserver, "accountchange")
-
start_and_wait(webserver, "acme-finished-http.example.test.target")
-
check_fullchain(webserver, "http.example.test")
-
check_issuer(webserver, "http.example.test", "pebble")
-
hash_after = webserver.succeed("sha256sum /var/lib/acme/http.example.test/cert.pem")
-
# Has to do a full run to register account, which creates new certs.
-
assert hash != hash_after
-
-
# Perform general tests
-
switch_to(webserver, "general")
-
-
with subtest("Can request certificate with HTTP-01 challenge"):
-
start_and_wait(webserver, "acme-finished-a.example.test.target")
-
check_fullchain(webserver, "a.example.test")
-
check_issuer(webserver, "a.example.test", "pebble")
-
webserver.wait_for_unit("nginx.service")
-
check_connection(client, "a.example.test")
-
-
with subtest("Runs 1 cert for account creation before others"):
-
start_and_wait(webserver, "acme-finished-b.example.test.target")
-
start_and_wait(webserver, "acme-finished-c.example.test.target")
-
check_connection(client, "b.example.test")
-
check_connection(client, "c.example.test")
-
-
with subtest("Certificates and accounts have safe + valid permissions"):
-
# Nginx will set the group appropriately when enableACME is used
-
group = "nginx"
-
webserver.succeed(
-
f"test $(stat -L -c '%a %U %G' /var/lib/acme/a.example.test/*.pem | tee /dev/stderr | grep '640 acme {group}' | wc -l) -eq 5"
-
)
-
webserver.succeed(
-
f"test $(stat -L -c '%a %U %G' /var/lib/acme/.lego/a.example.test/**/a.example.test* | tee /dev/stderr | grep '600 acme {group}' | wc -l) -eq 4"
-
)
-
webserver.succeed(
-
f"test $(stat -L -c '%a %U %G' /var/lib/acme/a.example.test | tee /dev/stderr | grep '750 acme {group}' | wc -l) -eq 1"
-
)
-
webserver.succeed(
-
f"test $(find /var/lib/acme/accounts -type f -exec stat -L -c '%a %U %G' {{}} \\; | tee /dev/stderr | grep -v '600 acme {group}' | wc -l) -eq 0"
-
)
-
-
# Selfsigned certs tests happen late so we aren't fighting the system init triggering cert renewal
-
with subtest("Can generate valid selfsigned certs"):
-
webserver.succeed("systemctl clean acme-a.example.test.service --what=state")
-
webserver.succeed("systemctl start acme-selfsigned-a.example.test.service")
-
check_fullchain(webserver, "a.example.test")
-
check_issuer(webserver, "a.example.test", "minica")
-
# Check selfsigned permissions
-
webserver.succeed(
-
f"test $(stat -L -c '%a %U %G' /var/lib/acme/a.example.test/*.pem | tee /dev/stderr | grep '640 acme {group}' | wc -l) -eq 5"
-
)
-
# Will succeed if nginx can load the certs
-
webserver.succeed("systemctl start nginx-config-reload.service")
-
-
with subtest("Correctly implements OCSP stapling"):
-
switch_to(webserver, "ocsp_stapling")
-
start_and_wait(webserver, "acme-finished-a.example.test.target")
-
check_stapling(client, "a.example.test")
-
-
with subtest("Can request certificate with HTTP-01 using lego's internal web server"):
-
switch_to(webserver, "lego_server")
-
start_and_wait(webserver, "acme-finished-lego.example.test.target")
-
webserver.wait_for_unit("nginx.service")
-
webserver.succeed("echo HENLO && systemctl cat nginx.service")
-
webserver.succeed('test "$(stat -c \'%U\' /var/lib/acme/* | uniq)" = "root"')
-
check_connection(client, "a.example.test")
-
check_connection(client, "lego.example.test")
-
-
with subtest("Can request certificate with HTTP-01 when nginx startup is delayed"):
-
webserver.execute("systemctl stop nginx")
-
switch_to(webserver, "slow_startup")
-
start_and_wait(webserver, "acme-finished-slow.example.test.target")
-
check_issuer(webserver, "slow.example.test", "pebble")
-
webserver.wait_for_unit("nginx.service")
-
check_connection(client, "slow.example.test")
-
-
with subtest("Can limit concurrency of running renewals"):
-
switch_to(webserver, "concurrency_limit")
-
start_and_wait(webserver, "acme-finished-f.example.test.target")
-
start_and_wait(webserver, "acme-finished-g.example.test.target")
-
start_and_wait(webserver, "acme-finished-h.example.test.target")
-
check_connection(client, "f.example.test")
-
check_connection(client, "g.example.test")
-
check_connection(client, "h.example.test")
-
-
with subtest("Works with caddy"):
-
switch_to(webserver, "caddy")
-
start_and_wait(webserver, "acme-finished-example.test.target")
-
webserver.wait_for_unit("caddy.service")
-
# FIXME reloading caddy is not sufficient to load new certs.
-
# Restart it manually until this is fixed.
-
webserver.succeed("systemctl restart caddy.service")
-
check_connection(client, "a.example.test")
-
-
with subtest("security.acme changes reflect on caddy"):
-
switch_to(webserver, "caddy_change_acme_conf")
-
start_and_wait(webserver, "acme-finished-example.test.target")
-
webserver.wait_for_unit("caddy.service")
-
# FIXME reloading caddy is not sufficient to load new certs.
-
# Restart it manually until this is fixed.
-
webserver.succeed("systemctl restart caddy.service")
-
check_connection_key_bits(client, "a.example.test", "384")
-
-
common_domains = ["http", "dns", "wildcard"]
-
for server, logsrc, domains in [
-
("nginx", "journalctl -n 30 -u nginx.service", common_domains + ["different-key"]),
-
("httpd", "tail -n 30 /var/log/httpd/*.log", common_domains),
-
]:
-
wait_for_server = lambda: webserver.wait_for_unit(f"{server}.service")
-
with subtest(f"Works with {server}"):
-
try:
-
switch_to(webserver, server)
-
for domain in domains:
-
if domain != "wildcard":
-
start_and_wait(
-
webserver,
-
f"acme-finished-{server}-{domain}.example.test.target"
-
)
-
except Exception as err:
-
_, output = webserver.execute(
-
f"{logsrc} && ls -al /var/lib/acme/acme-challenge"
-
)
-
print(output)
-
raise err
-
-
wait_for_server()
-
-
for domain in domains:
-
if domain != "wildcard":
-
check_issuer(webserver, f"{server}-{domain}.example.test", "pebble")
-
for domain in domains:
-
check_connection(client, f"{server}-{domain}.example.test")
-
check_connection(client, f"{server}-{domain}-alias.example.test")
-
-
test_domain = f"{server}-{domains[0]}.example.test"
-
-
with subtest(f"Can reload {server} when timer triggers renewal"):
-
# Switch to selfsigned first
-
webserver.succeed(f"systemctl clean acme-{test_domain}.service --what=state")
-
webserver.succeed(f"systemctl start acme-selfsigned-{test_domain}.service")
-
check_issuer(webserver, test_domain, "minica")
-
webserver.succeed(f"systemctl start {server}-config-reload.service")
-
webserver.succeed(f"systemctl start test-renew-{server}.target")
-
check_issuer(webserver, test_domain, "pebble")
-
check_connection(client, test_domain)
-
-
with subtest("Can remove an alias from a domain + cert is updated"):
-
test_alias = f"{server}-{domains[0]}-alias.example.test"
-
switch_to(webserver, f"{server}_remove_alias")
-
wait_for_server()
-
start_and_wait(webserver, f"acme-finished-{test_domain}.target")
-
wait_for_server()
-
check_connection(client, test_domain)
-
rc, _s = client.execute(
-
f"openssl s_client -CAfile /tmp/ca.crt -connect {test_alias}:443"
-
" </dev/null 2>/dev/null | openssl x509 -noout -text"
-
f" | grep DNS: | grep {test_alias}"
-
)
-
assert rc > 0, "Removed extraDomainName was not removed from the cert"
-
-
with subtest("security.acme changes reflect on web server"):
-
# Switch back to normal server config first, reset everything.
-
switch_to(webserver, server)
-
wait_for_server()
-
switch_to(webserver, f"{server}_change_acme_conf")
-
start_and_wait(webserver, f"acme-finished-{test_domain}.target")
-
wait_for_server()
-
check_connection_key_bits(client, test_domain, "384")
-
-
# Perform http-01 w/ lego test again, but using the pre-24.05 account hashing
-
# (see https://github.com/NixOS/nixpkgs/pull/317257)
-
with subtest("Check account hashing compatibility with pre-24.05 settings"):
-
webserver.succeed("rm -rf /var/lib/acme/.lego/accounts/*")
-
switch_to(webserver, "http01lego_legacyAccountHash", allow_fail=True)
-
# unit is failed, but in a way that this throws no exception:
-
try:
-
start_and_wait(webserver, "acme-finished-http.example.test.target")
-
except Exception:
-
# The unit is allowed – or even expected – to fail due to not being able to
-
# reach the actual letsencrypt server. We only use it for serialising the
-
# test execution, such that the account check is done after the service run
-
# involving the account creation has been executed at least once.
-
pass
-
hash = webserver.succeed("ls /var/lib/acme/.lego/accounts/")
-
print("Account hash: " + hash)
-
assert hash.strip() == "1ccf607d9aa280e9af00"
-
'';
-
}
+120
nixos/tests/acme/caddy.nix
···
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
+
let
+
domain = "example.test";
+
in
+
{
+
# Caddy only supports useACMEHost, hence we use a distinct test suite
+
name = "caddy";
+
meta = {
+
maintainers = lib.teams.acme.members;
+
# Hard timeout in seconds. Average run time is about 60 seconds.
+
timeout = 180;
+
};
+
+
nodes = {
+
# The fake ACME server which will respond to client requests
+
acme =
+
{ nodes, ... }:
+
{
+
imports = [ ../common/acme/server ];
+
};
+
+
caddy =
+
{ nodes, config, ... }:
+
let
+
fqdn = config.networking.fqdn;
+
in
+
{
+
imports = [ ../common/acme/client ];
+
networking.domain = domain;
+
networking.firewall.allowedTCPPorts = [
+
80
+
443
+
];
+
+
# Resolve the vhosts the easy way
+
networking.hosts."127.0.0.1" = [
+
"caddy-alt.${domain}"
+
];
+
+
# OpenSSL will be used for more thorough certificate validation
+
environment.systemPackages = [ pkgs.openssl ];
+
+
security.acme.certs."${fqdn}" = {
+
listenHTTP = ":8080";
+
reloadServices = [ "caddy.service" ];
+
};
+
+
users.users."${config.services.caddy.user}".extraGroups = [ "acme" ];
+
+
services.caddy = {
+
enable = true;
+
# FIXME reloading caddy is not sufficient to load new certs.
+
# Restart it manually until this is fixed.
+
enableReload = false;
+
globalConfig = ''
+
auto_https off
+
'';
+
virtualHosts."${fqdn}:443" = {
+
useACMEHost = fqdn;
+
};
+
virtualHosts.":80".extraConfig = ''
+
reverse_proxy localhost:8080
+
'';
+
};
+
+
specialisation.add_domain.configuration = {
+
security.acme.certs.${fqdn}.extraDomainNames = [
+
"caddy-alt.${domain}"
+
];
+
};
+
};
+
};
+
+
testScript =
+
{ nodes, ... }:
+
''
+
${(import ./utils.nix).pythonUtils}
+
+
domain = "${domain}"
+
ca_domain = "${nodes.acme.test-support.acme.caDomain}"
+
fqdn = "${nodes.caddy.networking.fqdn}"
+
+
acme.start()
+
wait_for_running(acme)
+
acme.wait_for_open_port(443)
+
+
with subtest("Boot and acquire a new cert"):
+
caddy.start()
+
wait_for_running(caddy)
+
+
check_issuer(caddy, fqdn, "pebble")
+
check_domain(caddy, fqdn, fqdn)
+
+
download_ca_certs(caddy, ca_domain)
+
check_connection(caddy, fqdn)
+
+
with subtest("Can run on selfsigned certificates"):
+
# Switch to selfsigned first
+
caddy.succeed(f"systemctl clean acme-{fqdn}.service --what=state")
+
caddy.succeed(f"systemctl start acme-selfsigned-{fqdn}.service")
+
check_issuer(caddy, fqdn, "minica")
+
caddy.succeed("systemctl restart caddy.service")
+
# Check that the web server has picked up the selfsigned cert
+
check_connection(caddy, fqdn, minica=True)
+
caddy.succeed(f"systemctl start acme-{fqdn}.service")
+
# This may fail a couple of times before caddy is restarted
+
check_issuer(caddy, fqdn, "pebble")
+
check_connection(caddy, fqdn)
+
+
with subtest("security.acme changes reflect on caddy"):
+
check_connection(caddy, f"caddy-alt.{domain}", fail=True)
+
switch_to(caddy, "add_domain")
+
check_connection(caddy, f"caddy-alt.{domain}")
+
'';
+
}
+56
nixos/tests/acme/default.nix
···
+
{ runTest }:
+
{
+
http01-builtin = runTest ./http01-builtin.nix;
+
dns01 = runTest ./dns01.nix;
+
caddy = runTest ./caddy.nix;
+
nginx = runTest (
+
import ./webserver.nix {
+
serverName = "nginx";
+
group = "nginx";
+
baseModule = {
+
services.nginx = {
+
enable = true;
+
enableReload = true;
+
logError = "stderr info";
+
# This tests a number of things at once:
+
# - Self-signed certs are in place before the webserver startup
+
# - Nginx is started before acme renewal is attempted
+
# - useACMEHost behaves as expected
+
# - acmeFallbackHost behaves as expected
+
virtualHosts.default = {
+
default = true;
+
addSSL = true;
+
useACMEHost = "proxied.example.test";
+
acmeFallbackHost = "localhost:8080";
+
# lego will refuse the request if the host header is not correct
+
extraConfig = ''
+
proxy_set_header Host $host;
+
'';
+
};
+
};
+
};
+
}
+
);
+
httpd = runTest (
+
import ./webserver.nix {
+
serverName = "httpd";
+
group = "wwwrun";
+
baseModule = {
+
services.httpd = {
+
enable = true;
+
# This is the default by virtue of being the first defined vhost.
+
virtualHosts.default = {
+
addSSL = true;
+
useACMEHost = "proxied.example.test";
+
locations."/.well-known/acme-challenge" = {
+
proxyPass = "http://localhost:8080/.well-known/acme-challenge";
+
extraConfig = ''
+
ProxyPreserveHost On
+
'';
+
};
+
};
+
};
+
};
+
}
+
);
+
}
+118
nixos/tests/acme/dns01.nix
···
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
+
let
+
domain = "example.test";
+
+
dnsServerIP = nodes: nodes.dnsserver.networking.primaryIPAddress;
+
+
dnsScript = pkgs.writeShellScript "dns-hook.sh" ''
+
set -euo pipefail
+
echo '[INFO]' "[$2]" 'dns-hook.sh' $*
+
if [ "$1" = "present" ]; then
+
${pkgs.curl}/bin/curl --data @- http://dnsserver.test:8055/set-txt << EOF
+
{"host": "$2", "value": "$3"}
+
EOF
+
else
+
${pkgs.curl}/bin/curl --data @- http://dnsserver.test:8055/clear-txt << EOF
+
{"host": "$2"}
+
EOF
+
fi
+
'';
+
in
+
{
+
name = "dns01";
+
meta = {
+
maintainers = lib.teams.acme.members;
+
# Hard timeout in seconds. Average run time is about 60 seconds.
+
timeout = 180;
+
};
+
+
nodes = {
+
# The fake ACME server which will respond to client requests
+
acme =
+
{ nodes, ... }:
+
{
+
imports = [ ../common/acme/server ];
+
networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ];
+
};
+
+
# A fake DNS server which can be configured with records as desired
+
# Used to test DNS-01 challenge
+
dnsserver =
+
{ nodes, ... }:
+
{
+
networking = {
+
firewall.allowedTCPPorts = [
+
8055
+
53
+
];
+
firewall.allowedUDPPorts = [ 53 ];
+
+
# nixos/lib/testing/network.nix will provide name resolution via /etc/hosts
+
# for all nodes based on their host names and domain
+
hostName = "dnsserver";
+
domain = "test";
+
};
+
systemd.services.pebble-challtestsrv = {
+
enable = true;
+
description = "Pebble ACME challenge test server";
+
wantedBy = [ "network.target" ];
+
serviceConfig = {
+
ExecStart = "${pkgs.pebble}/bin/pebble-challtestsrv -dns01 ':53' -defaultIPv6 '' -defaultIPv4 '${nodes.client.networking.primaryIPAddress}'";
+
# Required to bind on privileged ports.
+
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
+
};
+
};
+
};
+
+
client =
+
{ nodes, ... }:
+
{
+
imports = [ ../common/acme/client ];
+
networking.domain = domain;
+
networking.nameservers = lib.mkForce [ (dnsServerIP nodes) ];
+
+
# OpenSSL will be used for more thorough certificate validation
+
environment.systemPackages = [ pkgs.openssl ];
+
+
security.acme.certs."${domain}" = {
+
domain = "*.${domain}";
+
dnsProvider = "exec";
+
dnsPropagationCheck = false;
+
environmentFile = pkgs.writeText "wildcard.env" ''
+
EXEC_PATH=${dnsScript}
+
EXEC_POLLING_INTERVAL=1
+
EXEC_PROPAGATION_TIMEOUT=1
+
EXEC_SEQUENCE_INTERVAL=1
+
'';
+
};
+
};
+
};
+
+
testScript = ''
+
${(import ./utils.nix).pythonUtils}
+
+
cert = "${domain}"
+
+
dnsserver.start()
+
acme.start()
+
+
wait_for_running(dnsserver)
+
dnsserver.wait_for_open_port(53)
+
wait_for_running(acme)
+
acme.wait_for_open_port(443)
+
+
with subtest("Boot and acquire a new cert"):
+
client.start()
+
wait_for_running(client)
+
+
check_issuer(client, cert, "pebble")
+
check_domain(client, cert, cert, fail=True)
+
check_domain(client, cert, f"toodeep.nesting.{cert}", fail=True)
+
check_domain(client, cert, f"whatever.{cert}")
+
'';
+
}
+215
nixos/tests/acme/http01-builtin.nix
···
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
+
let
+
domain = "example.test";
+
in
+
{
+
name = "http01-builtin";
+
meta = {
+
maintainers = lib.teams.acme.members;
+
# Hard timeout in seconds. Average run time is about 90 seconds.
+
timeout = 300;
+
};
+
+
nodes = {
+
# The fake ACME server which will respond to client requests
+
acme =
+
{ nodes, ... }:
+
{
+
imports = [ ../common/acme/server ];
+
};
+
+
builtin =
+
{ nodes, config, ... }:
+
{
+
imports = [ ../common/acme/client ];
+
networking.domain = domain;
+
networking.firewall.allowedTCPPorts = [ 80 ];
+
+
# OpenSSL will be used for more thorough certificate validation
+
environment.systemPackages = [ pkgs.openssl ];
+
+
security.acme.certs."${config.networking.fqdn}" = {
+
listenHTTP = ":80";
+
};
+
+
specialisation = {
+
renew.configuration = {
+
# Pebble provides 5 year long certs,
+
# needs to be higher than that to test renewal
+
security.acme.certs."${config.networking.fqdn}".validMinDays = 9999;
+
};
+
+
accountchange.configuration = {
+
security.acme.certs."${config.networking.fqdn}".email = "admin@example.test";
+
};
+
+
keytype.configuration = {
+
security.acme.certs."${config.networking.fqdn}".keyType = "ec384";
+
};
+
+
# Perform http-01 test again, but using the pre-24.05 account hashing
+
# (see https://github.com/NixOS/nixpkgs/pull/317257)
+
# The hash is deterministic in this case - only based on keyType and email.
+
# Note: This test is making the assumption that the acme module will create
+
# the account directory regardless of internet connectivity or server reachability.
+
legacy_account_hash.configuration = {
+
security.acme.defaults.server = lib.mkForce null;
+
};
+
+
ocsp_stapling.configuration = {
+
security.acme.certs."${config.networking.fqdn}".ocspMustStaple = true;
+
};
+
+
preservation.configuration = { };
+
+
add_cert_and_domain.configuration = {
+
security.acme.certs = {
+
"${config.networking.fqdn}" = {
+
extraDomainNames = [
+
"builtin-alt.${domain}"
+
];
+
};
+
# We can assume that if renewal succeeds then the account creation leader
+
# logic is working, since only one service could bind to port 80 at the same time.
+
"builtin-2.${domain}".listenHTTP = ":80";
+
};
+
# To make sure it's the account creation leader that is doing the work.
+
security.acme.maxConcurrentRenewals = 10;
+
};
+
+
concurrency.configuration = {
+
# As above, relying on port binding behaviour to assert that concurrency limit
+
# prevents > 1 service running at a time.
+
security.acme.maxConcurrentRenewals = 1;
+
security.acme.certs = {
+
"${config.networking.fqdn}" = {
+
extraDomainNames = [
+
"builtin-alt.${domain}"
+
];
+
};
+
"builtin-2.${domain}" = {
+
extraDomainNames = [ "builtin-2-alt.${domain}" ];
+
listenHTTP = ":80";
+
};
+
"builtin-3.${domain}".listenHTTP = ":80";
+
};
+
};
+
};
+
};
+
};
+
+
testScript =
+
{ nodes, ... }:
+
let
+
certName = nodes.builtin.networking.fqdn;
+
caDomain = nodes.acme.test-support.acme.caDomain;
+
in
+
''
+
${(import ./utils.nix).pythonUtils}
+
+
domain = "${domain}"
+
cert = "${certName}"
+
cert2 = "builtin-2." + domain
+
cert3 = "builtin-3." + domain
+
legacy_account_dir = "/var/lib/acme/.lego/accounts/1ccf607d9aa280e9af00"
+
+
acme.start()
+
wait_for_running(acme)
+
acme.wait_for_open_port(443)
+
+
with subtest("Boot and acquire a new cert"):
+
builtin.start()
+
wait_for_running(builtin)
+
+
check_issuer(builtin, cert, "pebble")
+
check_domain(builtin, cert, cert)
+
+
with subtest("Validate permissions"):
+
check_permissions(builtin, cert, "acme")
+
+
with subtest("Check renewal behaviour"):
+
# First, test no-op behaviour
+
hash = builtin.succeed(f"sha256sum /var/lib/acme/{cert}/cert.pem")
+
# old_hash will be used in the preservation tests later
+
old_hash = hash
+
builtin.succeed(f"systemctl start acme-{cert}.service")
+
hash_after = builtin.succeed(f"sha256sum /var/lib/acme/{cert}/cert.pem")
+
assert hash == hash_after, "Certificate was unexpectedly changed"
+
+
switch_to(builtin, "renew")
+
check_issuer(builtin, cert, "pebble")
+
hash_after = builtin.succeed(f"sha256sum /var/lib/acme/{cert}/cert.pem | tee /dev/stderr")
+
assert hash != hash_after, "Certificate was not renewed"
+
+
with subtest("Handles email change correctly"):
+
hash = builtin.succeed(f"sha256sum /var/lib/acme/{cert}/cert.pem")
+
switch_to(builtin, "accountchange")
+
check_issuer(builtin, cert, "pebble")
+
# Check that there are now 2 account directories
+
builtin.succeed("test $(ls -1 /var/lib/acme/.lego/accounts | tee /dev/stderr | wc -l) -eq 2")
+
hash_after = builtin.succeed(f"sha256sum /var/lib/acme/{cert}/cert.pem")
+
# Has to do a full run to register account, which creates new certs.
+
assert hash != hash_after, "Certificate was not renewed"
+
# Remove the new account directory
+
builtin.succeed(
+
"cd /var/lib/acme/.lego/accounts"
+
" && ls -1 --sort=time | tee /dev/stderr | head -1 | xargs rm -rf"
+
)
+
# old_hash will be used in the preservation tests later
+
old_hash = hash_after
+
+
with subtest("Correctly implements OCSP stapling"):
+
check_stapling(builtin, cert, "${caDomain}", fail=True)
+
switch_to(builtin, "ocsp_stapling")
+
check_stapling(builtin, cert, "${caDomain}")
+
+
with subtest("Handles keyType change correctly"):
+
check_key_bits(builtin, cert, 256)
+
switch_to(builtin, "keytype")
+
check_key_bits(builtin, cert, 384)
+
# keyType is part of the accountHash, thus a new account will be created
+
builtin.succeed("test $(ls -1 /var/lib/acme/.lego/accounts | tee /dev/stderr | wc -l) -eq 2")
+
+
with subtest("Reuses generated, valid certs from previous configurations"):
+
# Right now, the hash should not match due to the previous test
+
hash = builtin.succeed(f"sha256sum /var/lib/acme/{cert}/cert.pem | tee /dev/stderr")
+
assert hash != old_hash, "Expected certificate to differ"
+
switch_to(builtin, "preservation")
+
hash = builtin.succeed(f"sha256sum /var/lib/acme/{cert}/cert.pem | tee /dev/stderr")
+
assert hash == old_hash, "Expected certificate to match from older configuration"
+
+
with subtest("Add a new cert, extend existing cert domains"):
+
check_domain(builtin, cert, f"builtin-alt.{domain}", fail=True)
+
switch_to(builtin, "add_cert_and_domain")
+
check_issuer(builtin, cert, "pebble")
+
check_domain(builtin, cert, f"builtin-alt.{domain}")
+
check_issuer(builtin, cert2, "pebble")
+
check_domain(builtin, cert2, cert2)
+
# There should not be a new account folder created
+
builtin.succeed("test $(ls -1 /var/lib/acme/.lego/accounts | tee /dev/stderr | wc -l) -eq 2")
+
+
with subtest("Check account hashing compatibility with pre-24.05 settings"):
+
switch_to(builtin, "legacy_account_hash", fail=True)
+
builtin.succeed(f"stat {legacy_account_dir} > /dev/stderr && rm -rf {legacy_account_dir}")
+
+
with subtest("Ensure Concurrency limits work"):
+
switch_to(builtin, "concurrency")
+
check_issuer(builtin, cert3, "pebble")
+
check_domain(builtin, cert3, cert3)
+
+
with subtest("Generate self-signed certs"):
+
check_issuer(builtin, cert, "pebble")
+
builtin.succeed(f"systemctl clean acme-{cert}.service --what=state")
+
builtin.succeed(f"systemctl start acme-selfsigned-{cert}.service")
+
check_issuer(builtin, cert, "minica")
+
check_domain(builtin, cert, cert)
+
+
with subtest("Validate permissions (self-signed)"):
+
check_permissions(builtin, cert, "acme")
+
'';
+
}
+166
nixos/tests/acme/python-utils.py
···
+
#!/usr/bin/env python3
+
import time
+
+
TOTAL_RETRIES = 20
+
+
+
def run(node, cmd, fail=False):
+
if fail:
+
return node.fail(cmd)
+
else:
+
return node.succeed(cmd)
+
+
# Waits for the system to finish booting or switching configuration
+
def wait_for_running(node):
+
node.succeed("systemctl is-system-running --wait")
+
+
# On first switch, this will create a symlink to the current system so that we can
+
# quickly switch between derivations
+
def switch_to(node, name, fail=False) -> None:
+
root_specs = "/tmp/specialisation"
+
node.execute(
+
f"test -e {root_specs}"
+
f" || ln -s $(readlink /run/current-system)/specialisation {root_specs}"
+
)
+
+
switcher_path = (
+
f"/run/current-system/specialisation/{name}/bin/switch-to-configuration"
+
)
+
rc, _ = node.execute(f"test -e '{switcher_path}'")
+
if rc > 0:
+
switcher_path = f"/tmp/specialisation/{name}/bin/switch-to-configuration"
+
+
cmd = f"{switcher_path} test"
+
run(node, cmd, fail=fail)
+
if not fail:
+
wait_for_running(node)
+
+
# Ensures the issuer of our cert matches the chain
+
# and matches the issuer we expect it to be.
+
# It's a good validation to ensure the cert.pem and fullchain.pem
+
# are not still selfsigned after verification
+
def check_issuer(node, cert_name, issuer) -> None:
+
for fname in ("cert.pem", "fullchain.pem"):
+
actual_issuer = node.succeed(
+
f"openssl x509 -noout -issuer -in /var/lib/acme/{cert_name}/{fname}"
+
).partition("=")[2]
+
assert (
+
issuer.lower() in actual_issuer.lower()
+
), f"{fname} issuer mismatch. Expected {issuer} got {actual_issuer}"
+
+
# Ensures the provided domain matches with the given cert
+
def check_domain(node, cert_name, domain, fail=False) -> None:
+
cmd = f"openssl x509 -noout -checkhost '{domain}' -in /var/lib/acme/{cert_name}/cert.pem"
+
run(node, cmd, fail=fail)
+
+
# Ensures the required values for OCSP stapling are present
+
# Pebble doesn't provide a full OCSP responder, so just checks the URL
+
def check_stapling(node, cert_name, ca_domain, fail=False):
+
rc, _ = node.execute(
+
f"openssl x509 -noout -ocsp_uri -in /var/lib/acme/{cert_name}/cert.pem"
+
f" | grep -i 'http://{ca_domain}:4002' 2>&1",
+
)
+
assert rc == 0 or fail, "Failed to find OCSP URI in issued certificate"
+
run(
+
node,
+
f"openssl x509 -noout -ext tlsfeature -in /var/lib/acme/{cert_name}/cert.pem"
+
f" | grep -iv 'no extensions' 2>&1",
+
fail=fail,
+
)
+
+
# Checks the keyType by validating the number of bits
+
def check_key_bits(node, cert_name, bits, fail=False):
+
run(
+
node,
+
f"openssl x509 -noout -text -in /var/lib/acme/{cert_name}/cert.pem"
+
f" | grep -i Public-Key | grep {bits} | tee /dev/stderr",
+
fail=fail,
+
)
+
+
# Ensure cert comes before chain in fullchain.pem
+
def check_fullchain(node, cert_name):
+
cert_file = f"/var/lib/acme/{cert_name}/fullchain.pem"
+
num_certs = node.succeed(f"grep -o 'END CERTIFICATE' {cert_file}")
+
assert len(num_certs.strip().split("\n")) > 1, "Insufficient certs in fullchain.pem"
+
+
first_cert_data = node.succeed(
+
f"grep -m1 -B50 'END CERTIFICATE' {cert_file}"
+
" | openssl x509 -noout -text"
+
)
+
for line in first_cert_data.lower().split("\n"):
+
if "dns:" in line:
+
print(f"First DNSName in fullchain.pem: {line}")
+
assert cert_name.lower() in line, f"{cert_name} not found in {line}"
+
return
+
+
assert False
+
+
# Checks the permissions in the cert directories are as expected
+
def check_permissions(node, cert_name, group):
+
stat = "stat -L -c '%a %U %G' "
+
node.succeed(
+
f"test $({stat} /var/lib/acme/{cert_name}/*.pem"
+
f" | tee /dev/stderr | grep -v '640 acme {group}' | wc -l) -eq 0"
+
)
+
node.succeed(
+
f"test $({stat} /var/lib/acme/.lego/{cert_name}/*/{cert_name}*"
+
f" | tee /dev/stderr | grep -v '600 acme {group}' | wc -l) -eq 0"
+
)
+
node.succeed(
+
f"test $({stat} /var/lib/acme/{cert_name}"
+
f" | tee /dev/stderr | grep -v '750 acme {group}' | wc -l) -eq 0"
+
)
+
node.succeed(
+
f"test $(find /var/lib/acme/.lego/accounts -type f -exec {stat} {{}} \\;"
+
f" | tee /dev/stderr | grep -v '600 acme {group}' | wc -l) -eq 0"
+
)
+
+
# BackoffTracker provides a robust system for handling test retries
+
class BackoffTracker:
+
delay = 1
+
increment = 1
+
+
def handle_fail(self, retries, message) -> int:
+
assert retries < TOTAL_RETRIES, message
+
+
print(f"Retrying in {self.delay}s, {retries + 1}/{TOTAL_RETRIES}")
+
time.sleep(self.delay)
+
+
# Only increment after the first try
+
if retries == 0:
+
self.delay += self.increment
+
self.increment *= 2
+
+
return retries + 1
+
+
def protect(self, func):
+
def wrapper(*args, retries: int = 0, **kwargs):
+
try:
+
return func(*args, **kwargs)
+
except Exception as err:
+
retries = self.handle_fail(retries, err.args)
+
return wrapper(*args, retries=retries, **kwargs)
+
+
return wrapper
+
+
+
backoff = BackoffTracker()
+
+
+
@backoff.protect
+
def download_ca_certs(node, ca_domain):
+
node.succeed(f"curl https://{ca_domain}:15000/roots/0 > /tmp/ca.crt")
+
node.succeed(f"curl https://{ca_domain}:15000/intermediate-keys/0 >> /tmp/ca.crt")
+
+
+
@backoff.protect
+
def check_connection(node, domain, fail=False, minica=False):
+
cafile = "/tmp/ca.crt"
+
if minica:
+
cafile = "/var/lib/acme/.minica/cert.pem"
+
run(node,
+
f"openssl s_client -brief -CAfile {cafile}"
+
f" -verify 2 -verify_return_error -verify_hostname {domain}"
+
f" -servername {domain} -connect {domain}:443 < /dev/null",
+
fail=fail,
+
)
+4
nixos/tests/acme/utils.nix
···
+
{
+
# Helper functions for python
+
pythonUtils = builtins.readFile ./python-utils.py;
+
}
+185
nixos/tests/acme/webserver.nix
···
+
{
+
serverName,
+
group,
+
baseModule,
+
domain ? "example.test",
+
}:
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
+
{
+
name = serverName;
+
meta = {
+
maintainers = lib.teams.acme.members;
+
# Hard timeout in seconds. Average run time is about 100 seconds.
+
timeout = 300;
+
};
+
+
nodes = {
+
# The fake ACME server which will respond to client requests
+
acme =
+
{ nodes, ... }:
+
{
+
imports = [ ../common/acme/server ];
+
};
+
+
webserver =
+
{ nodes, ... }:
+
{
+
imports = [
+
../common/acme/client
+
baseModule
+
];
+
networking.domain = domain;
+
networking.firewall.allowedTCPPorts = [
+
80
+
443
+
];
+
+
# Resolve the vhosts the easy way
+
networking.hosts."127.0.0.1" = [
+
"proxied.${domain}"
+
"certchange.${domain}"
+
"zeroconf.${domain}"
+
"zeroconf2.${domain}"
+
"nullroot.${domain}"
+
];
+
+
# OpenSSL will be used for more thorough certificate validation
+
environment.systemPackages = [ pkgs.openssl ];
+
+
# Used to determine if service reload was triggered.
+
# This does not provide a guarantee that the webserver is finished reloading,
+
# to handle that there is retry logic wrapping any connectivity checks.
+
systemd.targets."renew-triggered" = {
+
wantedBy = [ "${serverName}-config-reload.service" ];
+
after = [ "${serverName}-config-reload.service" ];
+
};
+
+
security.acme.certs."proxied.${domain}" = {
+
listenHTTP = ":8080";
+
group = group;
+
};
+
+
specialisation = {
+
# Test that the web server is correctly reloaded when the cert changes
+
certchange.configuration = {
+
security.acme.certs."proxied.${domain}".extraDomainNames = [
+
"certchange.${domain}"
+
];
+
};
+
+
# A useful transitional step before other tests, and tests behaviour
+
# of removing an extra domain from a cert.
+
certundo.configuration = { };
+
+
# Tests these features:
+
# - enableACME behaves as expected
+
# - serverAliases are appended to extraDomainNames
+
# - Correct routing to the specific virtualHost for a cert
+
# Inherits previous test config
+
zeroconf.configuration = {
+
services.${serverName}.virtualHosts."zeroconf.${domain}" = {
+
addSSL = true;
+
enableACME = true;
+
serverAliases = [ "zeroconf2.${domain}" ];
+
};
+
};
+
+
# Test that serverAliases are correctly removed which triggers
+
# cert regeneration and service reload.
+
rmalias.configuration = {
+
services.${serverName}.virtualHosts."zeroconf.${domain}" = {
+
addSSL = true;
+
enableACME = true;
+
};
+
};
+
+
# Test that "acmeRoot = null" still results in
+
# valid cert generation by inheriting defaults.
+
nullroot.configuration = {
+
security.acme.defaults.listenHTTP = ":8080";
+
services.${serverName}.virtualHosts."nullroot.${domain}" = {
+
onlySSL = true;
+
enableACME = true;
+
acmeRoot = null;
+
};
+
};
+
};
+
};
+
};
+
+
testScript =
+
{ nodes, ... }:
+
''
+
${(import ./utils.nix).pythonUtils}
+
+
domain = "${domain}"
+
ca_domain = "${nodes.acme.test-support.acme.caDomain}"
+
fqdn = f"proxied.{domain}"
+
+
acme.start()
+
wait_for_running(acme)
+
acme.wait_for_open_port(443)
+
+
with subtest("Acquire a cert through a proxied lego"):
+
webserver.start()
+
webserver.succeed("systemctl is-system-running --wait")
+
wait_for_running(webserver)
+
download_ca_certs(webserver, ca_domain)
+
check_connection(webserver, fqdn)
+
+
with subtest("Can run on selfsigned certificates"):
+
# Switch to selfsigned first
+
webserver.succeed(f"systemctl clean acme-{fqdn}.service --what=state")
+
webserver.succeed(f"systemctl start acme-selfsigned-{fqdn}.service")
+
check_issuer(webserver, fqdn, "minica")
+
webserver.succeed("systemctl restart ${serverName}-config-reload.service")
+
# Check that the web server has picked up the selfsigned cert
+
check_connection(webserver, fqdn, minica=True)
+
webserver.succeed("systemctl stop renew-triggered.target")
+
webserver.succeed(f"systemctl start acme-{fqdn}.service")
+
webserver.wait_for_unit("renew-triggered.target")
+
check_issuer(webserver, fqdn, "pebble")
+
check_connection(webserver, fqdn)
+
+
with subtest("security.acme changes reflect on web server part 1"):
+
check_connection(webserver, f"certchange.{domain}", fail=True)
+
switch_to(webserver, "certchange")
+
webserver.wait_for_unit("renew-triggered.target")
+
check_connection(webserver, f"certchange.{domain}")
+
check_connection(webserver, fqdn)
+
+
with subtest("security.acme changes reflect on web server part 2"):
+
check_connection(webserver, f"certchange.{domain}")
+
switch_to(webserver, "certundo")
+
webserver.wait_for_unit("renew-triggered.target")
+
check_connection(webserver, f"certchange.{domain}", fail=True)
+
check_connection(webserver, fqdn)
+
+
with subtest("Zero configuration SSL certificates for a vhost"):
+
check_connection(webserver, f"zeroconf.{domain}", fail=True)
+
switch_to(webserver, "zeroconf")
+
webserver.wait_for_unit("renew-triggered.target")
+
check_connection(webserver, f"zeroconf.{domain}")
+
check_connection(webserver, f"zeroconf2.{domain}")
+
check_connection(webserver, fqdn)
+
+
with subtest("Removing an alias from a vhost"):
+
check_connection(webserver, f"zeroconf2.{domain}")
+
switch_to(webserver, "rmalias")
+
webserver.wait_for_unit("renew-triggered.target")
+
check_connection(webserver, f"zeroconf2.{domain}", fail=True)
+
check_connection(webserver, f"zeroconf.{domain}")
+
check_connection(webserver, fqdn)
+
+
with subtest("Create cert using inherited default validation mechanism"):
+
check_connection(webserver, f"nullroot.{domain}", fail=True)
+
switch_to(webserver, "nullroot")
+
webserver.wait_for_unit("renew-triggered.target")
+
check_connection(webserver, f"nullroot.{domain}")
+
'';
+
}
+1 -1
nixos/tests/all-tests.nix
···
_3proxy = runTest ./3proxy.nix;
aaaaxy = runTest ./aaaaxy.nix;
-
acme = runTest ./acme.nix;
+
acme = import ./acme/default.nix { inherit runTest; };
acme-dns = handleTest ./acme-dns.nix {};
actual = handleTest ./actual.nix {};
adguardhome = runTest ./adguardhome.nix;
+46 -65
nixos/tests/common/acme/server/default.nix
···
# the test certificate into security.pki.certificateFiles or into package
# overlays.
#
-
# Another value that's needed if you don't use a custom resolver (see below for
-
# notes on that) is to add the acme node as a nameserver to every node
-
# that needs to acquire certificates using ACME, because otherwise the API host
-
# for acme.test can't be resolved.
-
#
-
# A configuration example of a full node setup using this would be this:
-
#
-
# {
-
# acme = import ./common/acme/server;
-
#
-
# example = { nodes, ... }: {
-
# networking.nameservers = [
-
# nodes.acme.networking.primaryIPAddress
-
# ];
-
# security.pki.certificateFiles = [
-
# nodes.acme.test-support.acme.caCert
-
# ];
-
# };
-
# }
-
#
-
# By default, this module runs a local resolver, generated using resolver.nix
-
# from the parent directory to automatically discover all zones in the network.
-
#
-
# If you do not want this and want to use your own resolver, you can just
-
# override networking.nameservers like this:
-
#
-
# {
-
# acme = { nodes, lib, ... }: {
-
# imports = [ ./common/acme/server ];
-
# networking.nameservers = lib.mkForce [
-
# nodes.myresolver.networking.primaryIPAddress
-
# ];
-
# };
-
#
-
# myresolver = ...;
-
# }
-
#
-
# Keep in mind, that currently only _one_ resolver is supported, if you have
-
# more than one resolver in networking.nameservers only the first one will be
-
# used.
-
#
-
# Also make sure that whenever you use a resolver from a different test node
-
# that it has to be started _before_ the ACME service.
+
# The hosts file of this node will be populated with a mapping of certificate
+
# domains (including extraDomainNames) to their parent nodes in the test suite.
+
# This negates the need for a DNS server for most testing. You can still specify
+
# a custom nameserver/resolver if necessary for other reasons.
{
config,
pkgs,
lib,
+
nodes ? { },
...
}:
let
···
in
{
-
imports = [ ../../resolver.nix ];
-
options.test-support.acme = {
caDomain = lib.mkOption {
type = lib.types.str;
···
};
config = {
-
test-support = {
-
resolver.enable =
+
networking = {
+
firewall.allowedTCPPorts = [
+
80
+
443
+
15000
+
4002
+
];
+
+
# Match the caDomain - nixos/lib/testing/network.nix will then add a record for us to
+
# all nodes in /etc/hosts
+
hostName = "acme";
+
domain = "test";
+
+
# Extend /etc/hosts to resolve all configured certificates to their hosts.
+
# This way, no DNS server will be needed to validate HTTP-01 certs.
+
hosts = lib.attrsets.concatMapAttrs (
+
_: node:
let
-
isLocalResolver = config.networking.nameservers == [ "127.0.0.1" ];
+
inherit (node.networking) primaryIPAddress primaryIPv6Address;
+
ips = builtins.filter (ip: ip != "") [
+
primaryIPAddress
+
primaryIPv6Address
+
];
+
names = lib.lists.unique (
+
lib.lists.flatten (
+
lib.lists.concatMap
+
(
+
cfg:
+
lib.attrsets.mapAttrsToList (
+
domain: cfg:
+
builtins.map (builtins.replaceStrings [ "*." ] [ "" ]) ([ domain ] ++ cfg.extraDomainNames)
+
) cfg.configuration.security.acme.certs
+
)
+
# A specialisation's config is nested under its configuration attribute.
+
# For ease of use, nest the root node's configuration simiarly.
+
([ { configuration = node; } ] ++ (builtins.attrValues node.specialisation))
+
)
+
);
in
-
lib.mkOverride 900 isLocalResolver;
+
builtins.listToAttrs (builtins.map (ip: lib.attrsets.nameValuePair ip names) ips)
+
) nodes;
};
-
# This has priority 140, because modules/testing/test-instrumentation.nix
-
# already overrides this with priority 150.
-
networking.nameservers = lib.mkOverride 140 [ "127.0.0.1" ];
-
networking.firewall.allowedTCPPorts = [
-
80
-
443
-
15000
-
4002
-
];
-
-
networking.extraHosts = ''
-
127.0.0.1 ${domain}
-
${config.networking.primaryIPAddress} ${domain}
-
'';
-
systemd.services = {
pebble = {
enable = true;
···
wantedBy = [ "network.target" ];
environment = {
# We're not testing lego, we're just testing our configuration.
-
# No need to sleep.
+
# No need to sleep or randomly fail nonces.
PEBBLE_VA_NOSLEEP = "1";
+
PEBBLE_WFE_NONCEREJECT = "0";
};
serviceConfig = {
-184
nixos/tests/common/resolver.nix
···
-
# This module automatically discovers zones in BIND and NSD NixOS
-
# configurations and creates zones for all definitions of networking.extraHosts
-
# (except those that point to 127.0.0.1 or ::1) within the current test network
-
# and delegates these zones using a fake root zone served by a BIND recursive
-
# name server.
-
{
-
config,
-
nodes,
-
pkgs,
-
lib,
-
...
-
}:
-
-
{
-
options.test-support.resolver.enable = lib.mkOption {
-
type = lib.types.bool;
-
default = true;
-
internal = true;
-
description = ''
-
Whether to enable the resolver that automatically discovers zone in the
-
test network.
-
-
This option is `true` by default, because the module
-
defining this option needs to be explicitly imported.
-
-
The reason this option exists is for the
-
{file}`nixos/tests/common/acme/server` module, which
-
needs that option to disable the resolver once the user has set its own
-
resolver.
-
'';
-
};
-
-
config = lib.mkIf config.test-support.resolver.enable {
-
networking.firewall.enable = false;
-
services.bind.enable = true;
-
services.bind.cacheNetworks = lib.mkForce [ "any" ];
-
services.bind.forwarders = lib.mkForce [ ];
-
services.bind.zones = lib.singleton {
-
name = ".";
-
master = true;
-
file =
-
let
-
addDot = zone: zone + lib.optionalString (!lib.hasSuffix "." zone) ".";
-
mkNsdZoneNames = zones: map addDot (lib.attrNames zones);
-
mkBindZoneNames = zones: map addDot (lib.attrNames zones);
-
getZones = cfg: mkNsdZoneNames cfg.services.nsd.zones ++ mkBindZoneNames cfg.services.bind.zones;
-
-
getZonesForNode = attrs: {
-
ip = attrs.config.networking.primaryIPAddress;
-
zones = lib.filter (zone: zone != ".") (getZones attrs.config);
-
};
-
-
zoneInfo = lib.mapAttrsToList (lib.const getZonesForNode) nodes;
-
-
# A and AAAA resource records for all the definitions of
-
# networking.extraHosts except those for 127.0.0.1 or ::1.
-
#
-
# The result is an attribute set with keys being the host name and the
-
# values are either { ipv4 = ADDR; } or { ipv6 = ADDR; } where ADDR is
-
# the IP address for the corresponding key.
-
recordsFromExtraHosts =
-
let
-
getHostsForNode = lib.const (n: n.config.networking.extraHosts);
-
allHostsList = lib.mapAttrsToList getHostsForNode nodes;
-
allHosts = lib.concatStringsSep "\n" allHostsList;
-
-
reIp = "[a-fA-F0-9.:]+";
-
reHost = "[a-zA-Z0-9.-]+";
-
-
matchAliases =
-
str:
-
let
-
matched = builtins.match "[ \t]+(${reHost})(.*)" str;
-
continue = lib.singleton (lib.head matched) ++ matchAliases (lib.last matched);
-
in
-
lib.optional (matched != null) continue;
-
-
matchLine =
-
str:
-
let
-
result = builtins.match "[ \t]*(${reIp})[ \t]+(${reHost})(.*)" str;
-
in
-
if result == null then
-
null
-
else
-
{
-
ipAddr = lib.head result;
-
hosts = lib.singleton (lib.elemAt result 1) ++ matchAliases (lib.last result);
-
};
-
-
skipLine =
-
str:
-
let
-
rest = builtins.match "[^\n]*\n(.*)" str;
-
in
-
if rest == null then "" else lib.head rest;
-
-
getEntries =
-
str: acc:
-
let
-
result = matchLine str;
-
next = getEntries (skipLine str);
-
newEntry = acc ++ lib.singleton result;
-
continue = if result == null then next acc else next newEntry;
-
in
-
if str == "" then acc else continue;
-
-
isIPv6 = str: builtins.match ".*:.*" str != null;
-
loopbackIps = [
-
"127.0.0.1"
-
"::1"
-
];
-
filterLoopback = lib.filter (e: !lib.elem e.ipAddr loopbackIps);
-
-
allEntries = lib.concatMap (
-
entry:
-
map (host: {
-
inherit host;
-
${if isIPv6 entry.ipAddr then "ipv6" else "ipv4"} = entry.ipAddr;
-
}) entry.hosts
-
) (filterLoopback (getEntries (allHosts + "\n") [ ]));
-
-
mkRecords =
-
entry:
-
let
-
records =
-
lib.optional (entry ? ipv6) "AAAA ${entry.ipv6}"
-
++ lib.optional (entry ? ipv4) "A ${entry.ipv4}";
-
mkRecord = typeAndData: "${entry.host}. IN ${typeAndData}";
-
in
-
lib.concatMapStringsSep "\n" mkRecord records;
-
-
in
-
lib.concatMapStringsSep "\n" mkRecords allEntries;
-
-
# All of the zones that are subdomains of existing zones.
-
# For example if there is only "example.com" the following zones would
-
# be 'subZones':
-
#
-
# * foo.example.com.
-
# * bar.example.com.
-
#
-
# While the following would *not* be 'subZones':
-
#
-
# * example.com.
-
# * com.
-
#
-
subZones =
-
let
-
allZones = lib.concatMap (zi: zi.zones) zoneInfo;
-
isSubZoneOf = z1: z2: lib.hasSuffix z2 z1 && z1 != z2;
-
in
-
lib.filter (z: lib.any (isSubZoneOf z) allZones) allZones;
-
-
# All the zones without 'subZones'.
-
filteredZoneInfo = map (
-
zi:
-
zi
-
// {
-
zones = lib.filter (x: !lib.elem x subZones) zi.zones;
-
}
-
) zoneInfo;
-
-
in
-
pkgs.writeText "fake-root.zone" ''
-
$TTL 3600
-
. IN SOA ns.fakedns. admin.fakedns. ( 1 3h 1h 1w 1d )
-
ns.fakedns. IN A ${config.networking.primaryIPAddress}
-
. IN NS ns.fakedns.
-
${lib.concatImapStrings (
-
num:
-
{ ip, zones }:
-
''
-
ns${toString num}.fakedns. IN A ${ip}
-
${lib.concatMapStrings (zone: ''
-
${zone} IN NS ns${toString num}.fakedns.
-
'') zones}
-
''
-
) (lib.filter (zi: zi.zones != [ ]) filteredZoneInfo)}
-
${recordsFromExtraHosts}
-
'';
-
};
-
};
-
}
+4 -1
nixos/tests/web-apps/gotosocial.nix
···
settings = {
host = "localhost:8081";
port = 8081;
+
instance-stats-mode = "serve";
};
};
};
···
machine.wait_for_unit("gotosocial.service")
machine.wait_for_unit("postgresql.service")
machine.wait_for_open_port(8081)
+
# Database migrations are running, wait until gotosocial no longer serves 503
+
machine.wait_until_succeeds("curl -sS -f http://localhost:8081/readyz", timeout=300)
# check user registration via cli
machine.succeed("gotosocial-admin account create --username nickname --email email@example.com --password kurtz575VPeBgjVm")
-
machine.succeed("curl -sS -f http://localhost:8081/nodeinfo/2.0 | jq '.usage.users.total' | grep -q '^1$'")
+
machine.wait_until_succeeds("curl -sS -f http://localhost:8081/nodeinfo/2.0 | jq '.usage.users.total' | grep -q '^1$'")
'';
}
+1
nixos/tests/web-servers/h2o/default.nix
···
{
basic = handleTestOn supportedSystems ./basic.nix { inherit system; };
mruby = handleTestOn supportedSystems ./mruby.nix { inherit system; };
+
tls-recommendations = handleTestOn supportedSystems ./tls-recommendations.nix { inherit system; };
}
+115
nixos/tests/web-servers/h2o/tls-recommendations.nix
···
+
import ../../make-test-python.nix (
+
{ lib, pkgs, ... }:
+
+
let
+
domain = "acme.test";
+
port = 8443;
+
+
hello_txt =
+
name:
+
pkgs.writeTextFile {
+
name = "/hello_${name}.txt";
+
text = "Hello, ${name}!";
+
};
+
+
mkH2OServer =
+
recommendations:
+
{ pkgs, lib, ... }:
+
{
+
services.h2o = {
+
enable = true;
+
package = pkgs.h2o.override (
+
lib.optionalAttrs
+
(builtins.elem recommendations [
+
"intermediate"
+
"old"
+
])
+
{
+
openssl = pkgs.openssl_legacy;
+
}
+
);
+
defaultTLSRecommendations = "modern"; # prove overridden
+
hosts = {
+
"${domain}" = {
+
tls = {
+
inherit port recommendations;
+
policy = "force";
+
identity = [
+
{
+
key-file = ../../common/acme/server/acme.test.key.pem;
+
certificate-file = ../../common/acme/server/acme.test.cert.pem;
+
}
+
];
+
};
+
settings = {
+
paths."/"."file.file" = "${hello_txt recommendations}";
+
};
+
};
+
};
+
settings = {
+
ssl-offload = "kernel";
+
};
+
};
+
+
security.pki.certificates = [
+
(builtins.readFile ../../common/acme/server/ca.cert.pem)
+
];
+
+
networking = {
+
firewall.allowedTCPPorts = [ port ];
+
extraHosts = "127.0.0.1 ${domain}";
+
};
+
};
+
in
+
{
+
name = "h2o-tls-recommendations";
+
+
meta = {
+
maintainers = with lib.maintainers; [ toastal ];
+
};
+
+
nodes = {
+
server_modern = mkH2OServer "modern";
+
server_intermediate = mkH2OServer "intermediate";
+
server_old = mkH2OServer "old";
+
};
+
+
testScript =
+
let
+
portStr = builtins.toString port;
+
in
+
# python
+
''
+
curl_basic = "curl -v --tlsv1.3 --http2 'https://${domain}:${portStr}/'"
+
curl_head = "curl -v --head 'https://${domain}:${portStr}/'"
+
curl_max_tls1_2 ="curl -v --tlsv1.0 --tls-max 1.2 'https://${domain}:${portStr}/'"
+
curl_max_tls1_2_intermediate_cipher ="curl -v --tlsv1.0 --tls-max 1.2 --ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256' 'https://${domain}:${portStr}/'"
+
curl_max_tls1_2_old_cipher ="curl -v --tlsv1.0 --tls-max 1.2 --ciphers 'ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256' 'https://${domain}:${portStr}/'"
+
+
server_modern.wait_for_unit("h2o.service")
+
modern_response = server_modern.succeed(curl_basic)
+
assert "Hello, modern!" in modern_response
+
modern_head = server_modern.succeed(curl_head)
+
assert "strict-transport-security" in modern_head
+
server_modern.fail(curl_max_tls1_2)
+
+
server_intermediate.wait_for_unit("h2o.service")
+
intermediate_response = server_intermediate.succeed(curl_basic)
+
assert "Hello, intermediate!" in intermediate_response
+
intermediate_head = server_modern.succeed(curl_head)
+
assert "strict-transport-security" in intermediate_head
+
server_intermediate.succeed(curl_max_tls1_2)
+
server_intermediate.succeed(curl_max_tls1_2_intermediate_cipher)
+
server_intermediate.fail(curl_max_tls1_2_old_cipher)
+
+
server_old.wait_for_unit("h2o.service")
+
old_response = server_old.succeed(curl_basic)
+
assert "Hello, old!" in old_response
+
old_head = server_modern.succeed(curl_head)
+
assert "strict-transport-security" in old_head
+
server_old.succeed(curl_max_tls1_2)
+
server_old.succeed(curl_max_tls1_2_intermediate_cipher)
+
server_old.succeed(curl_max_tls1_2_old_cipher)
+
'';
+
}
+
)
+3 -3
pkgs/applications/blockchains/polkadot/default.nix
···
in
rustPlatform.buildRustPackage rec {
pname = "polkadot";
-
version = "2412-1";
+
version = "2412-2";
src = fetchFromGitHub {
owner = "paritytech";
repo = "polkadot-sdk";
rev = "polkadot-stable${version}";
-
hash = "sha256-wvtK+xbq8MLx77ad+x8gzPyL5ScFxHFt6rlZUAzc0CU=";
+
hash = "sha256-iFlC/KQaq7RoQqnlOjIwxrMBdwarrNXfKyE2b1XXfzU=";
# the build process of polkadot requires a .git folder in order to determine
# the git commit hash that is being built and add it to the version string.
···
'';
useFetchCargoVendor = true;
-
cargoHash = "sha256-c88vE2DxjngARu0NSZR4NVEYK4OiSxIETVRY6K5CbVs=";
+
cargoHash = "sha256-P/pNmW6vQAB4gEDaPFYJQsqUlSBSX1krcU7C6ngRk5Q=";
buildType = "production";
buildAndTestSubdir = "polkadot";
+18 -16
pkgs/applications/misc/qelectrotech/default.nix
···
{
lib,
stdenv,
-
mkDerivation,
-
fetchzip,
+
fetchFromGitHub,
installShellFiles,
pkg-config,
qmake,
qtbase,
kcoreaddons,
kwidgetsaddons,
+
qtsvg,
+
wrapQtAppsHook,
}:
-
mkDerivation rec {
+
stdenv.mkDerivation rec {
pname = "qelectrotech";
-
version = "0.8.0";
+
version = "0.9.0";
-
src = fetchzip {
-
url = "https://git.tuxfamily.org/qet/qet.git/snapshot/qet-${version}.tar.gz";
-
sha256 = "sha256-op2vnMPF9bNnHGphWFB/HEeoThE6tX+9UvX8LWVwkzI=";
+
src = fetchFromGitHub {
+
owner = "qelectrotech";
+
repo = "qelectrotech-source-mirror";
+
tag = "0.9";
+
hash = "sha256-tj8q+mRVtdeDXbpiv4retdbNiIfvAFlutXn7BmjqFYU=";
};
postPatch = ''
substituteInPlace qelectrotech.pro \
-
--replace 'GIT_COMMIT_SHA="\\\"$(shell git -C \""$$_PRO_FILE_PWD_"\" rev-parse --verify HEAD)\\\""' \
+
--replace-fail 'GIT_COMMIT_SHA="\\\"$(shell git -C \""$$_PRO_FILE_PWD_"\" rev-parse --verify HEAD)\\\""' \
'GIT_COMMIT_SHA="\\\"${version}\\\""' \
-
--replace "COMPIL_PREFIX = '/usr/local/'" \
+
--replace-fail "COMPIL_PREFIX = '/usr/local/'" \
"COMPIL_PREFIX = '$out/'" \
-
--replace "INSTALL_PREFIX = '/usr/local/'" \
+
--replace-fail "INSTALL_PREFIX = '/usr/local/'" \
"INSTALL_PREFIX = '$out/'"
'';
···
installShellFiles
pkg-config
qmake
+
wrapQtAppsHook
];
buildInputs = [
kcoreaddons
kwidgetsaddons
qtbase
+
qtsvg
];
qmakeFlags = [
···
install -Dm555 qelectrotech $out/bin/qelectrotech
install -Dm444 -t $out/share/applications misc/qelectrotech.desktop
-
install -Dm444 -t $out/share/applications misc/x-qet-titleblock.desktop
-
install -Dm444 -t $out/share/applications misc/x-qet-element.desktop
-
install -Dm444 -t $out/share/applications misc/x-qet-project.desktop
mkdir -p $out/share/qelectrotech
cp -r elements $out/share/qelectrotech
···
runHook postInstall
'';
-
meta = with lib; {
+
meta = {
description = "Free software to create electric diagrams";
mainProgram = "qelectrotech";
homepage = "https://qelectrotech.org/";
-
license = licenses.gpl2;
-
maintainers = with maintainers; [ yvesf ];
+
license = lib.licenses.gpl2;
+
maintainers = with lib.maintainers; [ yvesf ];
platforms = qtbase.meta.platforms;
broken = stdenv.hostPlatform.isDarwin;
};
+2 -2
pkgs/applications/networking/browsers/firefox/wrapper.nix
···
-
{ stdenv, lib, makeDesktopItem, makeBinaryWrapper, lndir, config
+
{ stdenv, lib, makeDesktopItem, makeWrapper, lndir, config
, buildPackages
, jq, xdg-utils, writeText
···
};
}));
-
nativeBuildInputs = [ makeBinaryWrapper lndir jq ];
+
nativeBuildInputs = [ makeWrapper lndir jq ];
buildInputs = [ browser.gtk3 ];
makeWrapperArgs = [
+5 -2
pkgs/applications/science/math/glsurf/default.nix
···
]);
postPatch = ''
-
for f in callbacks*/Makefile src/Makefile; do
-
substituteInPlace "$f" --replace "+camlp4" \
+
for f in callbacks*/Makefile; do
+
substituteInPlace "$f" --replace-warn "+camlp4" \
"${ocamlPackages.camlp4}/lib/ocaml/${ocamlPackages.ocaml.version}/site-lib/camlp4"
done
# Fatal error: exception Sys_error("Mutex.unlock: Operation not permitted")
sed -i "/gl_started/d" src/draw.ml* src/main.ml
+
+
# Compatibility with camlimages ≥ 5.0.5
+
substituteInPlace src/Makefile --replace-warn camlimages.all_formats camlimages.core
'';
installPhase = ''
+9 -10
pkgs/applications/video/obs-studio/plugins/obs-source-switcher.nix
···
obs-studio,
}:
-
stdenv.mkDerivation rec {
+
stdenv.mkDerivation {
pname = "obs-source-switcher";
-
version = "0.4.1";
+
version = "0.4.3";
src = fetchFromGitHub {
owner = "exeldro";
repo = "obs-source-switcher";
-
rev = "8babf207d140e52114b6db63d98749d7a0a2758b";
-
sha256 = "sha256-J/NdIGsSXCtSOGF72pJZqqN5Y73eJfrA72LgZcTlP5o=";
+
rev = "b229f40faceb0bb39cea41ce0ce2f2f236c0cbd1";
+
hash = "sha256-5io2uMvPdHQAWFDqLyXLC6nxTEjkrk8v4v8XwGsPF7U=";
};
nativeBuildInputs = [ cmake ];
+
buildInputs = [ obs-studio ];
-
cmakeFlags = [
-
"-DBUILD_OUT_OF_TREE=On"
-
];
+
cmakeFlags = [ "-DBUILD_OUT_OF_TREE=On" ];
postInstall = ''
rm -rf $out/obs-plugins $out/data
'';
-
meta = with lib; {
+
meta = {
description = "Plugin for OBS Studio to switch between a list of sources";
homepage = "https://github.com/exeldro/obs-source-switcher";
-
maintainers = with maintainers; [ flexiondotorg ];
-
license = licenses.gpl2Plus;
+
maintainers = with lib.maintainers; [ flexiondotorg ];
+
license = lib.licenses.gpl2Plus;
platforms = [
"x86_64-linux"
"i686-linux"
+3 -3
pkgs/by-name/ai/aiken/package.nix
···
rustPlatform.buildRustPackage rec {
pname = "aiken";
-
version = "1.1.11";
+
version = "1.1.12";
src = fetchFromGitHub {
owner = "aiken-lang";
repo = "aiken";
rev = "v${version}";
-
hash = "sha256-RCMUF9+dj+zJM1tyuUs2oOMk+5xdHrzWYejuOPd/Ngc=";
+
hash = "sha256-QxIkqS+lqZjyuXUghSSd+4ud4Vrce7MgLrk2a5AQGmU=";
};
useFetchCargoVendor = true;
-
cargoHash = "sha256-R6jTr4F+ZbRracW8yQdpDHHerzbJy1jQYr/ttFSwCWc=";
+
cargoHash = "sha256-vbwYoBDTdAHCpCOJEdVo9Ir8wd4284pgYjyQuy/STHc=";
buildInputs =
[ openssl ]
+3 -3
pkgs/by-name/an/ankama-launcher/package.nix
···
}:
let
pname = "ankama-launcher";
-
version = "3.12.30";
+
version = "3.12.35";
# The original URL for the launcher is:
# https://launcher.cdn.ankama.com/installers/production/Ankama%20Launcher-Setup-x86_64.AppImage
# As it does not encode the version, we use the wayback machine (web.archive.org) to get a fixed URL.
# To update the client, head to web.archive.org and create a new snapshot of the download page.
src = fetchurl {
-
url = "https://web.archive.org/web/20250203095353/https://launcher.cdn.ankama.com/installers/production/Ankama%20Launcher-Setup-x86_64.AppImage";
-
hash = "sha256-qgWhC/xWUQpIoCNZcRxBPGpbNTFGysIF2N9a1MsxCFk=";
+
url = "https://web.archive.org/web/20250217184754/https://launcher.cdn.ankama.com/installers/production/Ankama%20Launcher-Setup-x86_64.AppImage";
+
hash = "sha256-/M5Wymahq0JWRgATRWoFoFFJ4qxu/cJvSqoz81/euug=";
};
appimageContents = appimageTools.extract { inherit pname version src; };
+3 -3
pkgs/by-name/ba/balena-cli/package.nix
···
in
buildNpmPackage' rec {
pname = "balena-cli";
-
version = "20.2.3";
+
version = "20.2.7";
src = fetchFromGitHub {
owner = "balena-io";
repo = "balena-cli";
rev = "v${version}";
-
hash = "sha256-sJksNbsGBzH1tjqAv0kX++J7NCj1fu64Bi/i3Tp8R9E=";
+
hash = "sha256-D6GZRkA+O6vuz1ntUT8Hz0fCEv6xlHx7sG6BrLzJm/k=";
};
-
npmDepsHash = "sha256-t1ZTQOuEvRcy9Zl9qqi8DDH2EVkjrBaTX4pTuRGl2ww=";
+
npmDepsHash = "sha256-x9/qwDJH0AlWehUlsFQdNHI53YWh2DHX/19VDA7vJc4=";
postPatch = ''
ln -s npm-shrinkwrap.json package-lock.json
+1
pkgs/by-name/ca/caddy/package.nix
···
command = "${caddy}/bin/caddy version";
package = caddy;
};
+
acme-integration = nixosTests.acme.caddy;
};
withPlugins = callPackage ./plugins.nix { inherit caddy; };
};
+7 -4
pkgs/by-name/cl/claude-code/package-lock.json
···
"packages": {
"": {
"dependencies": {
-
"@anthropic-ai/claude-code": "^0.2.9"
+
"@anthropic-ai/claude-code": "^0.2.14"
}
},
"node_modules/@anthropic-ai/claude-code": {
-
"version": "0.2.9",
-
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-0.2.9.tgz",
-
"integrity": "sha512-UGSEQbgDvhlEXC8rf5ASDXRSaq6Nfd4owY7k9bDdRhX9N5q8cMN+5vfTN1ezZhBcRFMOnpEK4eRSEgXW3eDeOQ==",
+
"version": "0.2.14",
+
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-0.2.14.tgz",
+
"integrity": "sha512-p729wIUq9/K/TecpE64nzWKEZJ1qddn20eQg1nUoMdEQtwWjwWYiecwJ6lDxCmNRWr0ukC7ovu0Kgtmh+uOPYg==",
"license": "SEE LICENSE IN README.md",
+
"os": [
+
"!win32"
+
],
"bin": {
"claude": "cli.mjs"
},
+3 -3
pkgs/by-name/cl/claude-code/package.nix
···
buildNpmPackage rec {
pname = "claude-code";
-
version = "0.2.9";
+
version = "0.2.14";
src = fetchzip {
url = "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-${version}.tgz";
-
hash = "sha256-NB+pfpXrjTvvs4o81dsLhyOCvDBCF6ANkAgTnxCaF9Q=";
+
hash = "sha256-Q/+Wa3RhY2XZc/0afAvCczLtnSVjoAWvhf2VEDHk+D8=";
};
-
npmDepsHash = "sha256-2v9wCcaOgA3RezX/pnqigsn6XhKcqP2adM2IGRhiHgc=";
+
npmDepsHash = "sha256-61JgKMXN03LIWcCi9CrSGhtOUJmRurnSSdt2iYHYkEs=";
postPatch = ''
cp ${./package-lock.json} package-lock.json
+24 -23
pkgs/by-name/co/cosmic-applibrary/package.nix
···
fetchFromGitHub,
stdenv,
rustPlatform,
+
libcosmicAppHook,
just,
-
pkg-config,
-
makeBinaryWrapper,
-
libxkbcommon,
-
wayland,
+
nix-update-script,
}:
rustPlatform.buildRustPackage rec {
pname = "cosmic-applibrary";
-
version = "0-unstable-2024-02-09";
+
version = "1.0.0-alpha.6";
src = fetchFromGitHub {
owner = "pop-os";
-
repo = pname;
-
rev = "e214e9867876c96b24568d8a45aaca2936269d9b";
-
hash = "sha256-fZxDRktiHHmj7X3e5VyJJMO081auOpSMSsBnJdhhtR8=";
+
repo = "cosmic-applibrary";
+
tag = "epoch-${version}";
+
hash = "sha256-hJOM5dZdLq6uYfhfspZzpbHgUOK/FWuIXuFPoisS8DU=";
};
useFetchCargoVendor = true;
-
cargoHash = "sha256-WCS1jCfnanILXGLq96+FI0gM1o4FIJQtSgZg86fe86E=";
+
cargoHash = "sha256-95jTSn0yYj2PNVtfumfD1rPf1yLXHUi60FBqENK8CSw=";
nativeBuildInputs = [
just
-
pkg-config
-
makeBinaryWrapper
-
];
-
buildInputs = [
-
libxkbcommon
-
wayland
+
libcosmicAppHook
];
dontUseJustBuild = true;
+
dontUseJustCheck = true;
justFlags = [
"--set"
···
substituteInPlace justfile --replace '#!/usr/bin/env' "#!$(command -v env)"
'';
-
postInstall = ''
-
wrapProgram $out/bin/cosmic-app-library \
-
--prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath [ wayland ]}"
-
'';
+
passthru.updateScript = nix-update-script {
+
extraArgs = [
+
"--version"
+
"unstable"
+
"--version-regex"
+
"epoch-(.*)"
+
];
+
};
-
meta = with lib; {
+
meta = {
homepage = "https://github.com/pop-os/cosmic-applibrary";
description = "Application Template for the COSMIC Desktop Environment";
-
license = licenses.gpl3Only;
-
maintainers = with maintainers; [ nyabinary ];
-
platforms = platforms.linux;
+
license = lib.licenses.gpl3Only;
+
maintainers = with lib.maintainers; [
+
nyabinary
+
HeitorAugustoLN
+
];
+
platforms = lib.platforms.linux;
mainProgram = "cosmic-app-library";
};
}
+2 -2
pkgs/by-name/ex/exiv2/package.nix
···
stdenv.mkDerivation rec {
pname = "exiv2";
-
version = "0.28.4";
+
version = "0.28.5";
outputs = [
"out"
···
owner = "exiv2";
repo = "exiv2";
rev = "v${version}";
-
hash = "sha256-3xSN1erwNn//jrpO3fAG/jVb2jOSIBQNDd7HHKecJQg=";
+
hash = "sha256-+Fe0+wkWWtM3MNgY6qp34/kC8jkOjOLusnd9WquYpA8=";
};
nativeBuildInputs = [
+108 -378
pkgs/by-name/fs/fsautocomplete/deps.json
···
"hash": "sha256-qLDLd8xZia6eDCt3PmZbnF6BtBBAHHYwe2CCPqTvSnY="
},
{
+
"pname": "fantomas",
+
"version": "7.0.0",
+
"hash": "sha256-v4bXmvjZOYxl5RSIHuqVfDzBQdRz5SrmzZtD6SeEYTY="
+
},
+
{
"pname": "Fantomas.Client",
"version": "0.9.1",
"hash": "sha256-KIHugHvwgaCVD/XQ0FjeZKhiSzzHJyovVPtM5IzttJI="
···
},
{
"pname": "FSharp.Analyzers.SDK",
-
"version": "0.28.0",
-
"hash": "sha256-3pB20Niv5q65D4BSCqkMUZk24MeO556kRlv0RXdUzYI="
+
"version": "0.29.0",
+
"hash": "sha256-hhyLzVyNO7lAm9RbQao15+d8gt9yYK2+Rem3yAoA+zw="
},
{
"pname": "FSharp.Compiler.Service",
-
"version": "43.9.100",
-
"hash": "sha256-9mMAYbS0c5Z/9SQ31NaBmDmZxr7hsX4144an5Yc8RgU="
+
"version": "43.9.201",
+
"hash": "sha256-YYo2O873Za3uXXdeOFCkIAToF8Txae1W2d5PuJ3dy/Y="
},
{
"pname": "FSharp.Control.AsyncSeq",
···
},
{
"pname": "FSharp.Core",
-
"version": "4.3.4",
-
"hash": "sha256-styyo+6mJy+yxE0NZG/b1hxkAjPOnJfMgd9zWzCJ5uk="
-
},
-
{
-
"pname": "FSharp.Core",
-
"version": "4.7.0",
-
"hash": "sha256-7aa4bga9XWLkq7J5KXv8Bilf1KGum77lSUqp+ooYIUg="
-
},
-
{
-
"pname": "FSharp.Core",
-
"version": "4.7.2",
-
"hash": "sha256-1eDe16w8+syA35AtrSiViMHQYgwBqmdViS4yCa4AMZ8="
-
},
-
{
-
"pname": "FSharp.Core",
-
"version": "5.0.1",
-
"hash": "sha256-WPkytjnHlThxzYlPvmpICMfR+4ANTiWNGjEA6LoAcBA="
-
},
-
{
-
"pname": "FSharp.Core",
-
"version": "6.0.0",
-
"hash": "sha256-aQDRgiGC7iTyzNEmvyd2RBCDcLG0I1dbfncHlkbeUMI="
-
},
-
{
-
"pname": "FSharp.Core",
-
"version": "6.0.1",
-
"hash": "sha256-Ehsgt3nCJijpaVuJguC1TPVEKSkJd6PSc07D2ZQSemI="
-
},
-
{
-
"pname": "FSharp.Core",
-
"version": "7.0.300",
-
"hash": "sha256-pDi6WWiwxmpceSUON1UWDNSOSDP8M5n0nSxF1yy59QQ="
-
},
-
{
-
"pname": "FSharp.Core",
-
"version": "8.0.101",
-
"hash": "sha256-MS6Z8dFRaGn1KxxCr6lScK3AQdksZ7VcphloE6dlL18="
-
},
-
{
-
"pname": "FSharp.Core",
-
"version": "9.0.100",
-
"hash": "sha256-V1q3CjbRvWZqxpi6cXD/R0F7pyXGGtH83M5Z/ITDrp8="
+
"version": "9.0.201",
+
"hash": "sha256-38Y0QFg/knogSJtxDVpVXKo5n4zAo8zaffeT6tbhahk="
},
{
"pname": "FSharp.Data.Adaptive",
-
"version": "1.2.16",
-
"hash": "sha256-H6kc8WMztWG03xISlMJolg8y+RSNdvPF7lY4biNuNfI="
+
"version": "1.2.18",
+
"hash": "sha256-xy1lJ+gZMlL1lIO42EVKpQHSU72yTezIaAQ76WwIvGo="
},
{
"pname": "FSharp.Formatting",
···
},
{
"pname": "Google.Protobuf",
-
"version": "3.22.5",
-
"hash": "sha256-KuPCqobX6vE9RYElAN9vw+FPonFipms7kE/cRDCLmSQ="
-
},
-
{
-
"pname": "Google.Protobuf",
"version": "3.28.3",
"hash": "sha256-jiA/FeYEEk/u9O1gtdnOzatym+/uHyaRJSdp34TOb1o="
},
···
"pname": "Grpc.Core.Api",
"version": "2.66.0",
"hash": "sha256-XVZmvlUK0t4bWaIBUAoAm007VhUdUvSSlCDh6P4IV9c="
-
},
-
{
-
"pname": "Grpc.Net.Client",
-
"version": "2.52.0",
-
"hash": "sha256-4Rhb8PIoV2BiohfRwzx1GYDPbcfqxGAmL2uB0atFFTk="
},
{
"pname": "Grpc.Net.Client",
···
},
{
"pname": "Ionide.ProjInfo",
-
"version": "0.68.0",
-
"hash": "sha256-aRkn1YxZquU+eTIZ7kLOmqnKxzCY/fm3sO539MlnnRw="
+
"version": "0.70.2",
+
"hash": "sha256-Y606emyiJ+ZgS5NH578WURySaVc/yo2OA7rWbOH57jA="
},
{
"pname": "Ionide.ProjInfo.FCS",
-
"version": "0.68.0",
-
"hash": "sha256-L/v/iwv0Lyrrb/53waIUtTUcRff0mHeNOvXI8FOpllE="
+
"version": "0.70.2",
+
"hash": "sha256-ufDkvztQZCtGTdegsIFIbixWTvIJwmNwhis2kb2OFU8="
},
{
"pname": "Ionide.ProjInfo.ProjectSystem",
-
"version": "0.68.0",
-
"hash": "sha256-rnoHnT3wSLrwrGa15Uod6PbpvX41i8yOjchxxQA/A44="
-
},
-
{
-
"pname": "Ionide.ProjInfo.Sln",
-
"version": "0.68.0",
-
"hash": "sha256-wtF91XZWw9W6X2ignQi0M5dxGY8pmaC+4fQWmhAjn8o="
+
"version": "0.70.2",
+
"hash": "sha256-3C+vn7ROgGWxjaHaACaUENYcAt9jYrnquBebQ2eo1y0="
},
{
"pname": "LinkDotNet.StringBuilder",
···
},
{
"pname": "MessagePack",
-
"version": "2.5.108",
-
"hash": "sha256-+vMXyEbfutY5WOFuFnNF24uLcKJTTdntVrVlSJH4yjI="
-
},
-
{
-
"pname": "MessagePack",
"version": "2.5.192",
"hash": "sha256-M9QUEAIeSoSgO3whVkOou0F8kbKCNJ7HHAvTZgytkPU="
},
···
"hash": "sha256-DLtncnaQ9Sp5YmWm89+2w3InhdU1ZQxnJgbonAq/1aM="
},
{
-
"pname": "Microsoft.AspNetCore.App.Ref",
-
"version": "6.0.36",
-
"hash": "sha256-9jDkWbjw/nd8yqdzVTagCuqr6owJ/DUMi4BlUZT4hWU="
-
},
-
{
-
"pname": "Microsoft.AspNetCore.App.Runtime.linux-arm64",
-
"version": "6.0.36",
-
"hash": "sha256-JQULJyF0ivLoUU1JaFfK/HHg+/qzpN7V2RR2Cc+WlQ4="
-
},
-
{
-
"pname": "Microsoft.AspNetCore.App.Runtime.linux-x64",
-
"version": "6.0.36",
-
"hash": "sha256-zUsVIpV481vMLAXaLEEUpEMA9/f1HGOnvaQnaWdzlyY="
-
},
-
{
-
"pname": "Microsoft.AspNetCore.App.Runtime.osx-arm64",
-
"version": "6.0.36",
-
"hash": "sha256-2seqZcz0JeUjkzh3QcGa9TcJ4LUafpFjTRk+Nm8T6T0="
-
},
-
{
-
"pname": "Microsoft.AspNetCore.App.Runtime.osx-x64",
-
"version": "6.0.36",
-
"hash": "sha256-yxLafxiBKkvfkDggPk0P9YZIHBkDJOsFTO7/V9mEHuU="
-
},
-
{
"pname": "Microsoft.Bcl.AsyncInterfaces",
-
"version": "8.0.0",
-
"hash": "sha256-9aWmiwMJKrKr9ohD1KSuol37y+jdDxPGJct3m2/Bknw="
-
},
-
{
-
"pname": "Microsoft.Bcl.AsyncInterfaces",
-
"version": "9.0.0",
-
"hash": "sha256-BsXNOWEgfFq3Yz7VTtK6m/ov4/erRqyBzieWSIpmc1U="
+
"version": "9.0.1",
+
"hash": "sha256-A3W2Hvhlf1ODx1NYWHwUyziZOGMaDPvXHZ/ubgNLYJA="
},
{
"pname": "Microsoft.Bcl.Cryptography",
-
"version": "9.0.0",
-
"hash": "sha256-yEji2HL9c5zgrNd0XsAwWTLIEi0z89hD0meJzneUoJM="
+
"version": "9.0.1",
+
"hash": "sha256-m0ZMScw9lHN+MnxgM5VpVUyOs0H09XmzqeYvCJ/1CG8="
},
{
"pname": "Microsoft.Bcl.HashCode",
···
"pname": "Microsoft.CodeAnalysis.Analyzers",
"version": "3.11.0",
"hash": "sha256-hQ2l6E6PO4m7i+ZsfFlEx+93UsLPo4IY3wDkNG11/Sw="
-
},
-
{
-
"pname": "Microsoft.CodeAnalysis.Analyzers",
-
"version": "3.3.4",
-
"hash": "sha256-qDzTfZBSCvAUu9gzq2k+LOvh6/eRvJ9++VCNck/ZpnE="
},
{
"pname": "Microsoft.CodeAnalysis.Common",
···
},
{
"pname": "Microsoft.Diagnostics.Tracing.TraceEvent",
-
"version": "3.1.17",
-
"hash": "sha256-vfVqz7GTFNC/JrrHgZ7WkPe/sNHyI29fq6XXS9ks2A4="
+
"version": "3.1.18",
+
"hash": "sha256-SabdlRagFuZRIBvJ6HwW6PyGbaGdYtC+7kqE1ygWqA8="
},
{
"pname": "Microsoft.DotNet.PlatformAbstractions",
···
},
{
"pname": "Microsoft.Extensions.Caching.Abstractions",
-
"version": "9.0.0",
-
"hash": "sha256-hDau5OMVGIg4sc5+ofe14ROqwt63T0NSbzm/Cv0pDrY="
+
"version": "9.0.1",
+
"hash": "sha256-4mBy8VC4rrDt8ZGyDCgpUqlHYxRTsAXh8H8HXXo6pd0="
},
{
"pname": "Microsoft.Extensions.Caching.Memory",
-
"version": "9.0.0",
-
"hash": "sha256-OZVOVGZOyv9uk5XGJrz6irBkPNjxnBxjfSyW30MnU0s="
+
"version": "9.0.1",
+
"hash": "sha256-NZV/R7g0CszBcE5Tjhfb2xX9i2rHT+Xc5QaxbhzRsg8="
},
{
"pname": "Microsoft.Extensions.Configuration",
-
"version": "9.0.0",
-
"hash": "sha256-uBLeb4z60y8z7NelHs9uT3cLD6wODkdwyfJm6/YZLDM="
+
"version": "9.0.1",
+
"hash": "sha256-QKWRIGi8RaZjheuW9gMouXa3oaL/nMwlmg28/xxEvgs="
},
{
"pname": "Microsoft.Extensions.Configuration.Abstractions",
-
"version": "9.0.0",
-
"hash": "sha256-xtG2USC9Qm0f2Nn6jkcklpyEDT3hcEZOxOwTc0ep7uc="
+
"version": "9.0.1",
+
"hash": "sha256-r3iWP+kwKo4Aib8SGo91kKWR5WusLrbFHUAw5uKQeNA="
},
{
"pname": "Microsoft.Extensions.Configuration.Binder",
-
"version": "9.0.0",
-
"hash": "sha256-6ajYWcNOQX2WqftgnoUmVtyvC1kkPOtTCif4AiKEffU="
+
"version": "9.0.1",
+
"hash": "sha256-uq6i0gTobSTqaNm/0XZuv8GGjFpnvgwXnCCPWl9FP9g="
},
{
"pname": "Microsoft.Extensions.DependencyInjection",
-
"version": "9.0.0",
-
"hash": "sha256-dAH52PPlTLn7X+1aI/7npdrDzMEFPMXRv4isV1a+14k="
+
"version": "9.0.1",
+
"hash": "sha256-Kt9fczXVeOIlvwuxXdQDKRfIZKClay0ESGUIAJpYiOw="
},
{
"pname": "Microsoft.Extensions.DependencyInjection.Abstractions",
-
"version": "9.0.0",
-
"hash": "sha256-CncVwkKZ5CsIG2O0+OM9qXuYXh3p6UGyueTHSLDVL+c="
-
},
-
{
-
"pname": "Microsoft.Extensions.DependencyModel",
-
"version": "5.0.0",
-
"hash": "sha256-vUwAWMxXiMW+JOiQE5fcJycOfJJzO87ESYAsEPsPqtY="
+
"version": "9.0.1",
+
"hash": "sha256-2tWVTPHsw1NG2zO0zsxvi1GybryqeE1V00ZRE66YZB4="
},
{
"pname": "Microsoft.Extensions.DependencyModel",
-
"version": "9.0.0",
-
"hash": "sha256-xirwlMWM0hBqgTneQOGkZ8l45mHT08XuSSRIbprgq94="
+
"version": "9.0.1",
+
"hash": "sha256-x+1GlrYNoN+UsooEUV2cr8TPPyx5Ni/vB6pJpspM8kw="
},
{
"pname": "Microsoft.Extensions.Diagnostics.Abstractions",
-
"version": "9.0.0",
-
"hash": "sha256-wG1LcET+MPRjUdz3HIOTHVEnbG/INFJUqzPErCM79eY="
+
"version": "9.0.1",
+
"hash": "sha256-/JkeyAQ//lfuHrWbq8ZFrHZiLvIXSnBj0MG0rU8eggQ="
},
{
"pname": "Microsoft.Extensions.Logging",
-
"version": "9.0.0",
-
"hash": "sha256-kR16c+N8nQrWeYLajqnXPg7RiXjZMSFLnKLEs4VfjcM="
+
"version": "9.0.1",
+
"hash": "sha256-IjszwetJ/r1NvwVyh+/SlavabNt9UXf3ZSGP9gGwnkk="
},
{
"pname": "Microsoft.Extensions.Logging.Abstractions",
-
"version": "6.0.0",
-
"hash": "sha256-QNqcQ3x+MOK7lXbWkCzSOWa/2QyYNbdM/OEEbWN15Sw="
-
},
-
{
-
"pname": "Microsoft.Extensions.Logging.Abstractions",
-
"version": "9.0.0",
-
"hash": "sha256-iBTs9twjWXFeERt4CErkIIcoJZU1jrd1RWCI8V5j7KU="
+
"version": "9.0.1",
+
"hash": "sha256-aFZeUno9yLLbvtrj53gA7oD41vxZZYkrJhlOghpMEjo="
},
{
"pname": "Microsoft.Extensions.Logging.Configuration",
-
"version": "9.0.0",
-
"hash": "sha256-ysPjBq64p6JM4EmeVndryXnhLWHYYszzlVpPxRWkUkw="
+
"version": "9.0.1",
+
"hash": "sha256-O7LaPjMs2PslVj/BUcXb1LeKFRClJZ6AsxlpdKxnng4="
},
{
"pname": "Microsoft.Extensions.Options",
-
"version": "9.0.0",
-
"hash": "sha256-DT5euAQY/ItB5LPI8WIp6Dnd0lSvBRP35vFkOXC68ck="
+
"version": "9.0.1",
+
"hash": "sha256-wOKd/0+kRK3WrGA2HmS/KNYUTUwXHmTAD5IsClTFA10="
},
{
"pname": "Microsoft.Extensions.Options.ConfigurationExtensions",
-
"version": "9.0.0",
-
"hash": "sha256-r1Z3sEVSIjeH2UKj+KMj86har68g/zybSqoSjESBcoA="
+
"version": "9.0.1",
+
"hash": "sha256-pzc49CPyBlSoyflWvW6J+xqk2RXEVfPczcDiR0Aj9xA="
},
{
"pname": "Microsoft.Extensions.Primitives",
-
"version": "9.0.0",
-
"hash": "sha256-ZNLusK1CRuq5BZYZMDqaz04PIKScE2Z7sS2tehU7EJs="
+
"version": "9.0.1",
+
"hash": "sha256-tdbtoC7eQGW5yh66FWCJQqmFJkNJD+9e6DDKTs7YAjs="
},
{
"pname": "Microsoft.NET.StringTools",
···
"hash": "sha256-cZnnBoiUIZOGMUy31sIMAn3gAd4VgSBFFe8pNfzQBtA="
},
{
-
"pname": "Microsoft.NET.StringTools",
-
"version": "17.6.3",
-
"hash": "sha256-H2Qw8x47WyFOd/VmgRmGMc+uXySgUv68UISgK8Frsjw="
-
},
-
{
"pname": "Microsoft.NET.Test.Sdk",
"version": "17.12.0",
"hash": "sha256-DKFEbhh2wPzahNeHdEoFig8tZh/LEVrFc5+zpT43Btg="
},
{
-
"pname": "Microsoft.NETCore.App.Host.linux-arm64",
-
"version": "6.0.36",
-
"hash": "sha256-9lC/LYnthYhjkWWz2kkFCvlA5LJOv11jdt59SDnpdy0="
-
},
-
{
-
"pname": "Microsoft.NETCore.App.Host.linux-x64",
-
"version": "6.0.36",
-
"hash": "sha256-VFRDzx7LJuvI5yzKdGmw/31NYVbwHWPKQvueQt5xc10="
-
},
-
{
-
"pname": "Microsoft.NETCore.App.Host.osx-arm64",
-
"version": "6.0.36",
-
"hash": "sha256-DaSWwYACJGolEBuMhzDVCj/rQTdDt061xCVi+gyQnuo="
-
},
-
{
-
"pname": "Microsoft.NETCore.App.Host.osx-x64",
-
"version": "6.0.36",
-
"hash": "sha256-FrRny9EI6HKCKQbu6mcLj5w4ooSRrODD4Vj2ZMGnMd4="
-
},
-
{
-
"pname": "Microsoft.NETCore.App.Ref",
-
"version": "6.0.36",
-
"hash": "sha256-9LZgVoIFF8qNyUu8kdJrYGLutMF/cL2K82HN2ywwlx8="
-
},
-
{
-
"pname": "Microsoft.NETCore.App.Runtime.linux-arm64",
-
"version": "6.0.36",
-
"hash": "sha256-k3rxvUhCEU0pVH8KgEMtkPiSOibn+nBh+0zT2xIfId8="
-
},
-
{
-
"pname": "Microsoft.NETCore.App.Runtime.linux-x64",
-
"version": "6.0.36",
-
"hash": "sha256-U8wJ2snSDFqeAgDVLXjnniidC7Cr5aJ1/h/BMSlyu0c="
-
},
-
{
-
"pname": "Microsoft.NETCore.App.Runtime.osx-arm64",
-
"version": "6.0.36",
-
"hash": "sha256-UfLcrL2Gj/OLz0s92Oo+OCJeDpZFAcQLPLiSNND8D5Y="
-
},
-
{
-
"pname": "Microsoft.NETCore.App.Runtime.osx-x64",
-
"version": "6.0.36",
-
"hash": "sha256-0xIJYFzxdMcnCj3wzkFRQZSnQcPHzPHMzePRIOA3oJs="
-
},
-
{
"pname": "Microsoft.NETCore.Platforms",
"version": "5.0.0",
"hash": "sha256-LIcg1StDcQLPOABp4JRXIs837d7z0ia6+++3SF3jl1c="
···
"hash": "sha256-rf8Sh0fQq44Sneuvs64unkkIHg8kOjDGWE35j9iLx5I="
},
{
-
"pname": "Microsoft.VisualStudio.Threading",
-
"version": "17.10.48",
-
"hash": "sha256-WL8c7TjDBHGjsVLMMPf9cin8rirzOdxusEBQlkUfiVU="
+
"pname": "Microsoft.VisualStudio.SolutionPersistence",
+
"version": "1.0.28",
+
"hash": "sha256-7BmeidGDb19wfvpaYA91luCFE1CStb8iLlnyJhVCJ6w="
},
{
"pname": "Microsoft.VisualStudio.Threading",
···
},
{
"pname": "Microsoft.VisualStudio.Threading.Analyzers",
-
"version": "17.10.48",
-
"hash": "sha256-EvZGbyxtrJDvHZwsQbZDXtVfWiy0f58oCdTdSzD34wI="
-
},
-
{
-
"pname": "Microsoft.VisualStudio.Threading.Analyzers",
"version": "17.12.19",
"hash": "sha256-7EteBGfUDOOpDihazJ4XGuPA2dvdc7HkpV8zTVl3FdQ="
},
···
},
{
"pname": "Nerdbank.Streams",
-
"version": "2.11.74",
-
"hash": "sha256-asIdaqCIjZspTA+hhtjKNajpCo+ZQi3erZLCpBQ5No4="
-
},
-
{
-
"pname": "Nerdbank.Streams",
"version": "2.11.79",
"hash": "sha256-1bzibVcSH8LJMR8Nb6Q0q/7fieTgxRnVY4C1RvRbrrI="
-
},
-
{
-
"pname": "Newtonsoft.Json",
-
"version": "13.0.1",
-
"hash": "sha256-K2tSVW4n4beRPzPu3rlVaBEMdGvWSv/3Q1fxaDh4Mjo="
},
{
"pname": "Newtonsoft.Json",
···
"pname": "OpenTelemetry.Api",
"version": "1.10.0",
"hash": "sha256-ZSpQFnNgkk3dO8Q7yokJ/VSl4wp5PuIv9nduxgC6UxU="
-
},
-
{
-
"pname": "OpenTelemetry.Api",
-
"version": "1.9.0",
-
"hash": "sha256-raXpHi2DZ3mSLn9dnJYF64XaP23epdfu8zgagSpm8+4="
},
{
"pname": "OpenTelemetry.Api.ProviderBuilderExtensions",
···
},
{
"pname": "Serilog",
-
"version": "3.1.1",
-
"hash": "sha256-L263y8jkn7dNFD2jAUK6mgvyRTqFe39i1tRhVZsNZTI="
-
},
-
{
-
"pname": "Serilog",
-
"version": "4.0.0",
-
"hash": "sha256-j8hQ5TdL1TjfdGiBO9PyHJFMMPvATHWN1dtrrUZZlNw="
-
},
-
{
-
"pname": "Serilog",
"version": "4.1.0",
"hash": "sha256-r89nJ5JE5uZlsRrfB8QJQ1byVVfCWQbySKQ/m9PYj0k="
},
···
},
{
"pname": "StreamJsonRpc",
-
"version": "2.16.36",
-
"hash": "sha256-XLCQsY7xu67E8E7WJIvjHtk3iobREPCiljW8jNpfi68="
-
},
-
{
-
"pname": "StreamJsonRpc",
-
"version": "2.20.17",
-
"hash": "sha256-0uUM1JUC6NLjQOPhpEIKCt0zkd/Sh8FjMCjI2j+TYxw="
-
},
-
{
-
"pname": "StreamJsonRpc",
-
"version": "2.8.28",
-
"hash": "sha256-iMesOucDwxjGDw2cBKDDzxZskjC1Mc0bszI/frB6qpA="
-
},
-
{
-
"pname": "System.Buffers",
-
"version": "4.5.1",
-
"hash": "sha256-wws90sfi9M7kuCPWkv1CEYMJtCqx9QB/kj0ymlsNaxI="
+
"version": "2.20.20",
+
"hash": "sha256-t0DVjJejQBPk+LukHVuXSe0M2QWpDJt7W7q4DRR4OI4="
},
{
"pname": "System.Buffers",
···
},
{
"pname": "System.Collections.Immutable",
-
"version": "6.0.0",
-
"hash": "sha256-DKEbpFqXCIEfqp9p3ezqadn5b/S1YTk32/EQK+tEScs="
-
},
-
{
-
"pname": "System.Collections.Immutable",
-
"version": "8.0.0",
-
"hash": "sha256-F7OVjKNwpqbUh8lTidbqJWYi476nsq9n+6k0+QVRo3w="
-
},
-
{
-
"pname": "System.Collections.Immutable",
-
"version": "9.0.0",
-
"hash": "sha256-+6q5VMeoc5bm4WFsoV6nBXA9dV5pa/O4yW+gOdi8yac="
+
"version": "9.0.1",
+
"hash": "sha256-23od93P7QHRzlL5NtKNUDujiYGeXRkkw7lXkyN4zOGY="
},
{
"pname": "System.CommandLine",
···
},
{
"pname": "System.Composition",
-
"version": "8.0.0",
-
"hash": "sha256-rA118MFj6soKN++BvD3y9gXAJf0lZJAtGARuznG5+Xg="
-
},
-
{
-
"pname": "System.Composition",
-
"version": "9.0.0",
-
"hash": "sha256-FehOkQ2u1p8mQ0/wn3cZ+24HjhTLdck8VZYWA1CcgbM="
+
"version": "9.0.1",
+
"hash": "sha256-KUJnkjMNNJ2b/1664xflRM9AmAs00PkmXLfbTnNdL6U="
},
{
"pname": "System.Composition.AttributedModel",
-
"version": "9.0.0",
-
"hash": "sha256-a7y7H6zj+kmYkllNHA402DoVfY9IaqC3Ooys8Vzl24M="
+
"version": "9.0.1",
+
"hash": "sha256-93Q8jFzbF0tEhk+UAHoULA+DWiwgrKiBxcm8k/dQ75M="
},
{
"pname": "System.Composition.Convention",
-
"version": "9.0.0",
-
"hash": "sha256-tw4vE5JRQ60ubTZBbxoMPhtjOQCC3XoDFUH7NHO7o8U="
+
"version": "9.0.1",
+
"hash": "sha256-FQihbXN3FkEiPprS5XLDIbYLye3RI0yHba099oDuIxY="
},
{
"pname": "System.Composition.Hosting",
-
"version": "9.0.0",
-
"hash": "sha256-oOxU+DPEEfMCuNLgW6wSkZp0JY5gYt44FJNnWt+967s="
+
"version": "9.0.1",
+
"hash": "sha256-jas95z5TZo4VtLIev6Ixqfh0Xv54CI7zCzJ8867bpVE="
},
{
"pname": "System.Composition.Runtime",
-
"version": "9.0.0",
-
"hash": "sha256-AyIe+di1TqwUBbSJ/sJ8Q8tzsnTN+VBdJw4K8xZz43s="
+
"version": "9.0.1",
+
"hash": "sha256-wAciMAsH7WR8ETiulgFhBRLBihdNEXb8Zjtzook2UQ4="
},
{
"pname": "System.Composition.TypedParts",
-
"version": "9.0.0",
-
"hash": "sha256-F5fpTUs3Rr7yP/NyIzr+Xn5NdTXXp8rrjBnF9UBBUog="
+
"version": "9.0.1",
+
"hash": "sha256-fyLCfmGKgUSIpizl6OiSX61Td9xmLjii/PDQAowCihc="
},
{
"pname": "System.Configuration.ConfigurationManager",
-
"version": "8.0.0",
-
"hash": "sha256-xhljqSkNQk8DMkEOBSYnn9lzCSEDDq4yO910itptqiE="
-
},
-
{
-
"pname": "System.Configuration.ConfigurationManager",
-
"version": "9.0.0",
-
"hash": "sha256-+pLnTC0YDP6Kjw5DVBiFrV/Q3x5is/+6N6vAtjvhVWk="
+
"version": "9.0.1",
+
"hash": "sha256-jsaxEBDfR4/N6v0lS4dOCLhu6/+IcqYtWL0Y8kCa10s="
},
{
"pname": "System.Diagnostics.Debug",
···
},
{
"pname": "System.Diagnostics.DiagnosticSource",
-
"version": "8.0.0",
-
"hash": "sha256-+aODaDEQMqla5RYZeq0Lh66j+xkPYxykrVvSCmJQ+Vs="
-
},
-
{
-
"pname": "System.Diagnostics.DiagnosticSource",
-
"version": "9.0.0",
-
"hash": "sha256-1VzO9i8Uq2KlTw1wnCCrEdABPZuB2JBD5gBsMTFTSvE="
+
"version": "9.0.1",
+
"hash": "sha256-nIIvVK+5uyOhAuU2sERNADK4N/A/x0MilBH/EAr1gOA="
},
{
"pname": "System.Diagnostics.EventLog",
-
"version": "9.0.0",
-
"hash": "sha256-tPvt6yoAp56sK/fe+/ei8M65eavY2UUhRnbrREj/Ems="
+
"version": "9.0.1",
+
"hash": "sha256-pqqZyEm/9Gsp1zT5WJeVX47S2MvoiGGgG1S2Ud/aIqo="
},
{
"pname": "System.Diagnostics.Tracing",
···
},
{
"pname": "System.Formats.Asn1",
-
"version": "9.0.0",
-
"hash": "sha256-LHyYt3rzKjPzxCHW/cynkBBzkl4I2h5wW6WYWG0k1w4="
+
"version": "9.0.1",
+
"hash": "sha256-6IrTTFmnKDz3jkfDQ9wvfGJ2Cb8TIAt9h1Xj5cKCJ5Q="
},
{
"pname": "System.Formats.Nrbf",
-
"version": "9.0.0",
-
"hash": "sha256-c4qf6CocQUZB0ySGQd8s15PXY7xfrjQqMGXxkwytKyw="
+
"version": "9.0.1",
+
"hash": "sha256-AyJTbJRuDeynPxO8C9ciDJqFGE+Quktas+naqvJfcjk="
},
{
"pname": "System.Globalization",
···
},
{
"pname": "System.IO.Pipelines",
-
"version": "8.0.0",
-
"hash": "sha256-LdpB1s4vQzsOODaxiKstLks57X9DTD5D6cPx8DE1wwE="
-
},
-
{
-
"pname": "System.IO.Pipelines",
-
"version": "9.0.0",
-
"hash": "sha256-vb0NrPjfEao3kfZ0tavp2J/29XnsQTJgXv3/qaAwwz0="
+
"version": "9.0.1",
+
"hash": "sha256-CnmDanknCGbNnoDjgZw62M/Grg8IMTJDa8x3P07UR2A="
},
{
"pname": "System.Linq",
···
"pname": "System.Management",
"version": "8.0.0",
"hash": "sha256-HwpfDb++q7/vxR6q57mGFgl5U0vxy+oRJ6orFKORfP0="
-
},
-
{
-
"pname": "System.Memory",
-
"version": "4.5.5",
-
"hash": "sha256-EPQ9o1Kin7KzGI5O3U3PUQAZTItSbk9h/i4rViN3WiI="
},
{
"pname": "System.Memory",
···
},
{
"pname": "System.Numerics.Vectors",
-
"version": "4.5.0",
-
"hash": "sha256-qdSTIFgf2htPS+YhLGjAGiLN8igCYJnCCo6r78+Q+c8="
-
},
-
{
-
"pname": "System.Numerics.Vectors",
"version": "4.6.0",
"hash": "sha256-fKS3uWQ2HmR69vNhDHqPLYNOt3qpjiWQOXZDHvRE1HU="
},
···
},
{
"pname": "System.Reflection.Emit.Lightweight",
-
"version": "4.6.0",
-
"hash": "sha256-913OIkt3v3N12Yke328IRxTtgYUQYNs/eSzOs8wUPkM="
-
},
-
{
-
"pname": "System.Reflection.Emit.Lightweight",
"version": "4.7.0",
"hash": "sha256-V0Wz/UUoNIHdTGS9e1TR89u58zJjo/wPUWw6VaVyclU="
},
{
"pname": "System.Reflection.Metadata",
-
"version": "6.0.0",
-
"hash": "sha256-VJHXPjP05w6RE/Swu8wa2hilEWuji3g9bl/6lBMSC/Q="
-
},
-
{
-
"pname": "System.Reflection.Metadata",
-
"version": "8.0.0",
-
"hash": "sha256-dQGC30JauIDWNWXMrSNOJncVa1umR1sijazYwUDdSIE="
-
},
-
{
-
"pname": "System.Reflection.Metadata",
-
"version": "9.0.0",
-
"hash": "sha256-avEWbcCh7XgpsSesnR3/SgxWi/6C5OxjR89Jf/SfRjQ="
+
"version": "9.0.1",
+
"hash": "sha256-7YYxXbXteRDhIFNbJbJ3jcYWOTArHJmyA6KpUyxNmbw="
},
{
"pname": "System.Reflection.MetadataLoadContext",
-
"version": "9.0.0",
-
"hash": "sha256-voF8Csk1WLPikMRrKmGxUOtM9k6W4RB2JAfwjsaF8oo="
+
"version": "9.0.1",
+
"hash": "sha256-kWm31a0unw/H8SjxaabVYKInR40bTAL9JnGQEVQGTsU="
},
{
"pname": "System.Reflection.Primitives",
···
},
{
"pname": "System.Resources.Extensions",
-
"version": "9.0.0",
-
"hash": "sha256-y2gLEMuAy6QfEyNJxABC/ayMWGnwlpX735jsUQLktho="
+
"version": "9.0.1",
+
"hash": "sha256-fHBGEo8jNIfsvrQsFYrtBdBxUq/M/MAA10+LKlSs7Ss="
},
{
"pname": "System.Resources.ResourceManager",
···
"pname": "System.Runtime",
"version": "4.3.1",
"hash": "sha256-R9T68AzS1PJJ7v6ARz9vo88pKL1dWqLOANg4pkQjkA0="
-
},
-
{
-
"pname": "System.Runtime.CompilerServices.Unsafe",
-
"version": "6.0.0",
-
"hash": "sha256-bEG1PnDp7uKYz/OgLOWs3RWwQSVYm+AnPwVmAmcgp2I="
},
{
"pname": "System.Runtime.CompilerServices.Unsafe",
···
},
{
"pname": "System.Security.Cryptography.Pkcs",
-
"version": "9.0.0",
-
"hash": "sha256-AjG14mGeSc2Ka4QSelGBM1LrGBW3VJX60lnihKyJjGY="
+
"version": "9.0.1",
+
"hash": "sha256-8j3cZYi1A9r9ssP+mZ/vmoVziXxQfdVmmvVpzacrtYo="
},
{
"pname": "System.Security.Cryptography.Primitives",
···
},
{
"pname": "System.Security.Cryptography.ProtectedData",
-
"version": "9.0.0",
-
"hash": "sha256-gPgPU7k/InTqmXoRzQfUMEKL3QuTnOKowFqmXTnWaBQ="
+
"version": "9.0.1",
+
"hash": "sha256-U2c8q6vD+O42zNTGRSAbsdj+r3+nXegoDYdB/Qm4nvU="
},
{
"pname": "System.Security.Cryptography.X509Certificates",
···
},
{
"pname": "System.Security.Cryptography.Xml",
-
"version": "9.0.0",
-
"hash": "sha256-SQJWwAFrJUddEU6JiZB52FM9tGjRlJAYH8oYVzG5IJU="
+
"version": "9.0.1",
+
"hash": "sha256-7kxs0pLGYK5OqUHsXCaHgEbuHFw6PnWagZweChxiGBk="
},
{
"pname": "System.Security.Principal.Windows",
···
},
"pname": "System.Text.Encoding.CodePages",
-
"version": "7.0.0",
-
"hash": "sha256-eCKTVwumD051ZEcoJcDVRGnIGAsEvKpfH3ydKluHxmo="
-
},
-
{
-
"pname": "System.Text.Encoding.CodePages",
-
"version": "9.0.0",
-
"hash": "sha256-OvtGrWDjuXdcIuMV504IDiBq9g8vtRIcn5w25x4W9HE="
+
"version": "9.0.1",
+
"hash": "sha256-9ZQvC7M87NJ7dFRkBHE30yWXftKdcmFbSTkdazmxx2w="
},
"pname": "System.Text.Encodings.Web",
-
"version": "9.0.0",
-
"hash": "sha256-WGaUklQEJywoGR2jtCEs5bxdvYu5SHaQchd6s4RE5x0="
+
"version": "9.0.1",
+
"hash": "sha256-iuAVcTiiZQLCZjDfDqdLLPHqZdZqvFabwLFHiVYdRJo="
},
"pname": "System.Text.Json",
-
"version": "9.0.0",
-
"hash": "sha256-aM5Dh4okLnDv940zmoFAzRmqZre83uQBtGOImJpoIqk="
+
"version": "9.0.1",
+
"hash": "sha256-2dqE+Mx5eJZ8db74ofUiUXHOSxDCmXw5n9VC9w4fUr0="
},
"pname": "System.Text.RegularExpressions",
···
},
"pname": "System.Threading.Channels",
-
"version": "7.0.0",
-
"hash": "sha256-Cu0gjQsLIR8Yvh0B4cOPJSYVq10a+3F9pVz/C43CNeM="
-
},
-
{
-
"pname": "System.Threading.Channels",
-
"version": "9.0.0",
-
"hash": "sha256-depIorJqzjyWew0+aBRgbGh88KWivbp9RrtWZHFr+pI="
+
"version": "9.0.1",
+
"hash": "sha256-vhMteiuJiLPcKFAPsi9Nlx9WDyO+ZyzrLe+ZwvulD3g="
},
"pname": "System.Threading.Tasks",
···
},
"pname": "System.Threading.Tasks.Dataflow",
-
"version": "9.0.0",
-
"hash": "sha256-nRzcFvLBpcOfyIJdCCZq5vDKZN0xHVuB8yCXoMrwZJA="
-
},
-
{
-
"pname": "System.Threading.Tasks.Extensions",
-
"version": "4.5.4",
-
"hash": "sha256-owSpY8wHlsUXn5xrfYAiu847L6fAKethlvYx97Ri1ng="
+
"version": "9.0.1",
+
"hash": "sha256-8U695SoAQZlWXHXVfiGpG9qePQUb9SfqaBRYYeaFaGY="
},
"pname": "System.Threading.Tasks.Extensions",
+13 -5
pkgs/by-name/fs/fsautocomplete/package.nix
···
fetchFromGitHub,
dotnetCorePackages,
testers,
+
_experimental-update-script-combinators,
+
nix-update-script,
}:
-
buildDotnetModule (finalAttrs: rec {
+
buildDotnetModule (finalAttrs: {
pname = "fsautocomplete";
-
version = "0.75.0";
+
version = "0.77.2";
src = fetchFromGitHub {
owner = "fsharp";
repo = "FsAutoComplete";
-
rev = "v${version}";
-
hash = "sha256-+IkoXj7l6a/iPigIVy334XiwQFm/pD63FWpV2r0x84c=";
+
rev = "v${finalAttrs.version}";
+
hash = "sha256-rCfiWzVsK9lvo4uMNrgWdXsjrvBQDZOyFpKxKdbT/3g=";
};
nugetDeps = ./deps.json;
···
useDotnetFromEnv = true;
-
passthru.tests.version = testers.testVersion { package = finalAttrs.finalPackage; };
+
passthru = {
+
tests.version = testers.testVersion { package = finalAttrs.finalPackage; };
+
updateScript = _experimental-update-script-combinators.sequence [
+
(nix-update-script { })
+
finalAttrs.passthru.fetch-deps
+
];
+
};
meta = with lib; {
description = "FsAutoComplete project (FSAC) provides a backend service for rich editing or intellisense features for editors";
+11 -9
pkgs/by-name/gi/git-big-picture/package.nix
···
{
lib,
python3Packages,
-
fetchPypi,
+
fetchFromGitHub,
git,
graphviz,
}:
python3Packages.buildPythonApplication rec {
pname = "git-big-picture";
-
version = "1.1.1";
-
format = "wheel";
+
version = "1.3.0";
+
pyproject = true;
-
src = fetchPypi {
-
inherit format version;
-
pname = "git_big_picture"; # underscores needed for working download URL
-
python = "py3"; # i.e. no Python 2.7
-
sha256 = "a20a480057ced1585c4c38497d27a5012f12dd29697313f0bb8fa6ddbb5c17d8";
+
src = fetchFromGitHub {
+
owner = "git-big-picture";
+
repo = "git-big-picture";
+
tag = "v${version}";
+
hash = "sha256-aBwSw7smeRkkXSPY02Cs+jFI1wvgj1JisUny+R8G59E=";
};
+
+
build-system = with python3Packages; [ setuptools ];
postFixup = ''
wrapProgram $out/bin/git-big-picture \
-
--prefix PATH ":" ${
+
--prefix PATH : ${
lib.makeBinPath [
git
graphviz
+5 -5
pkgs/by-name/go/google-chrome/package.nix
···
linux = stdenv.mkDerivation (finalAttrs: {
inherit pname meta passthru;
-
version = "133.0.6943.126";
+
version = "133.0.6943.141";
src = fetchurl {
url = "https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${finalAttrs.version}-1_amd64.deb";
-
hash = "sha256-B1kPoPMgPOwusxosWzge4d6jQwV0A10fwL2gcrzYu/4=";
+
hash = "sha256-iMW9V3dARAG7kJgCCWIT0OBDsxYqDnp+CpwgcjAT8zI=";
};
# With strictDeps on, some shebangs were not being patched correctly
···
darwin = stdenvNoCC.mkDerivation (finalAttrs: {
inherit pname meta passthru;
-
version = "133.0.6943.127";
+
version = "133.0.6943.142";
src = fetchurl {
-
url = "http://dl.google.com/release2/chrome/ac4k2kxfjhk4ggh2tvxk4t47hl7q_133.0.6943.127/GoogleChrome-133.0.6943.127.dmg";
-
hash = "sha256-baWFgjwcHg1Y3jNdIpcARFoaK0GZgqFe0bM2xtXsJtA=";
+
url = "http://dl.google.com/release2/chrome/ejubi5y7bpapjhrevtxffszuji_133.0.6943.142/GoogleChrome-133.0.6943.142.dmg";
+
hash = "sha256-cDnCnswvtRkyrwv52/GFHPTsmRZItN3rgqqc62mS7Ao=";
};
dontPatch = true;
+3 -3
pkgs/by-name/go/gotosocial/package.nix
···
owner = "superseriousbusiness";
repo = "gotosocial";
-
version = "0.17.4";
+
version = "0.18.1";
web-assets = fetchurl {
url = "https://github.com/${owner}/${repo}/releases/download/v${version}/${repo}_${version}_web-assets.tar.gz";
-
hash = "sha256-esip1xGB0NroYRlKLNEs/o3J2G2nQyOIZTdDpVuY5Ag=";
+
hash = "sha256-5MSABLPyTbFMTno9vUDvLT9h7oQM6eNUuwD+dsHiCLo=";
};
in
buildGoModule rec {
···
src = fetchFromGitHub {
inherit owner repo;
tag = "v${version}";
-
hash = "sha256-OikJkTc2UK74eGy8AjEAk8cyRL57QReM0J6tXr9EAjw=";
+
hash = "sha256-4jV1G1HwpIST2Y27RAhJB3CoJevwuhxdzi615hj0Qv0=";
};
vendorHash = null;
+4 -4
pkgs/by-name/ho/hoppscotch/package.nix
···
let
pname = "hoppscotch";
-
version = "24.12.0-0";
+
version = "25.1.1-0";
src =
fetchurl
{
aarch64-darwin = {
url = "https://github.com/hoppscotch/releases/releases/download/v${version}/Hoppscotch_mac_aarch64.dmg";
-
hash = "sha256-pM9s5rRb/VswJECK44Ku2rSa4a7kpKhJM9t6uR/6B9A=";
+
hash = "sha256-1KYc96WUlybXhgPeT97w1mLE2zxmohIhvNMCmEb5Vf0=";
};
x86_64-darwin = {
url = "https://github.com/hoppscotch/releases/releases/download/v${version}/Hoppscotch_mac_x64.dmg";
-
hash = "sha256-vXZSeogWBJt7ev0bmWB5MWYcePgq1noG3djU8kjZumQ=";
+
hash = "sha256-wdqgzTXFL7Dvq1DOrjyPE4O3OYfpvmRSLzk+HBJIaTU=";
};
x86_64-linux = {
url = "https://github.com/hoppscotch/releases/releases/download/v${version}/Hoppscotch_linux_x64.AppImage";
-
hash = "sha256-iwSqGcaQqFawGhT4vWKQp63ZoHGjssNYJ3ByvKJacU0=";
+
hash = "sha256-M9fQx4NBotLPe8i43E1uqHpFeoXdHGQePp4zgzbzDdM=";
};
}
.${stdenv.system} or (throw "Unsupported system: ${stdenv.system}");
+2 -2
pkgs/by-name/ku/kubo/package.nix
···
buildGoModule rec {
pname = "kubo";
-
version = "0.33.1"; # When updating, also check if the repo version changed and adjust repoVersion below
+
version = "0.33.2"; # When updating, also check if the repo version changed and adjust repoVersion below
rev = "v${version}";
passthru.repoVersion = "16"; # Also update kubo-migrator when changing the repo version
···
# Kubo makes changes to its source tarball that don't match the git source.
src = fetchurl {
url = "https://github.com/ipfs/kubo/releases/download/${rev}/kubo-source.tar.gz";
-
hash = "sha256-ybsQ25tuRPCp5XKZyxpq/a0x/h69P41+SLukro1xuMc=";
+
hash = "sha256-PSXX1mLteeLpaq74PnWeDy+E6E3uVWr1c5+FTMBKc5g=";
};
# tarball contains multiple files/directories
+4 -1
pkgs/by-name/le/lego/package.nix
···
mainProgram = "lego";
};
-
passthru.tests.lego = nixosTests.acme;
+
passthru.tests = {
+
lego-http = nixosTests.acme.http01-builtin;
+
lego-dns = nixosTests.acme.dns01-builtin;
+
};
}
+81
pkgs/by-name/op/open-adventure/package.nix
···
+
{
+
lib,
+
stdenv,
+
fetchFromGitLab,
+
python3Packages,
+
pkg-config,
+
libedit,
+
cppcheck,
+
coreutils,
+
asciidoctor,
+
}:
+
+
stdenv.mkDerivation (finalAttrs: {
+
pname = "open-adventure";
+
version = "1.20";
+
src = fetchFromGitLab {
+
owner = "esr";
+
repo = "open-adventure";
+
tag = finalAttrs.version;
+
hash = "sha256-xbsMz99CNLhpM6BSJVcRzxPB6tUYfPy/3Z+8BKt8b1E=";
+
};
+
+
nativeBuildInputs = [
+
python3Packages.python
+
pkg-config
+
asciidoctor
+
];
+
+
buildInputs = [
+
python3Packages.pyyaml
+
libedit
+
];
+
+
doCheck = true;
+
+
nativeCheckInputs = [
+
python3Packages.pylint
+
cppcheck
+
];
+
+
postPatch = ''
+
patchShebangs --build make_dungeon.py
+
+
# https://gitlab.com/esr/open-adventure/-/issues/70
+
substituteInPlace Makefile --replace-fail "--template " "--template="
+
+
substituteInPlace tests/tapview --replace-fail "/bin/echo" ${lib.getExe' coreutils "echo"}
+
'';
+
+
buildFlags = [
+
"advent.6"
+
];
+
+
installPhase = ''
+
runHook preInstall
+
+
mkdir -vp "$out/bin" "$out/share/man/man6" "$out/share/applications/" "$out/share/icons/hicolor/scalable/apps"
+
install -m 555 ./advent $out/bin
+
install -m 444 ./advent.6 $out/share/man/man6
+
install -m 444 ./advent.desktop $out/share/applications
+
install -m 444 ./advent.svg $out/share/icons/hicolor/scalable/apps
+
+
runHook postInstall
+
'';
+
+
meta = {
+
description = "Forward-port of the Crowther/Woods Adventure 2.5 game from 1995";
+
longDescription = ''
+
This code is a forward-port of the Crowther/Woods Adventure 2.5 game
+
from 1995, last version in the main line of Colossal Cave Adventure
+
development written by Crowther and Woods. The authors have given
+
permission and encouragement to this release.
+
'';
+
license = lib.licenses.bsd2;
+
mainProgram = "advent";
+
homepage = "http://www.catb.org/~esr/open-adventure/";
+
changelog = "https://gitlab.com/esr/open-adventure/-/blob/${finalAttrs.version}/NEWS.adoc";
+
maintainers = with lib.maintainers; [ EmanuelM153 ];
+
platforms = lib.platforms.all;
+
};
+
})
+2 -1
pkgs/by-name/pe/pebble/package.nix
···
];
passthru.tests = {
-
smoke-test = nixosTests.acme;
+
smoke-test-http = nixosTests.acme.http01-builtin;
+
smoke-test-dns = nixosTests.acme.dns01;
};
meta = {
+11 -10
pkgs/by-name/ra/radsecproxy/package.nix
···
stdenv.mkDerivation rec {
pname = "radsecproxy";
-
version = "1.9.3";
+
version = "1.11.1";
src = fetchFromGitHub {
-
owner = pname;
-
repo = pname;
-
rev = version;
-
sha256 = "sha256-4w5aQIh3loHrxFGhWt6pW2jgj/JuqQSYmNsnAkEuKoI=";
+
owner = "radsecproxy";
+
repo = "radsecproxy";
+
tag = version;
+
hash = "sha256-2+NDcz2RGRa30+XXS/PT5rjjKJYEnibYY3mVWjDv7Jk=";
};
nativeBuildInputs = [ autoreconfHook ];
+
buildInputs = [
openssl
nettle
];
configureFlags = [
-
"--with-ssl=${openssl.dev}"
+
"--with-openssl=${openssl.dev}"
"--sysconfdir=/etc"
"--localstatedir=/var"
];
-
meta = with lib; {
+
meta = {
homepage = "https://radsecproxy.github.io/";
description = "Generic RADIUS proxy that supports both UDP and TLS (RadSec) RADIUS transports";
-
license = licenses.bsd3;
-
maintainers = with maintainers; [ sargon ];
-
platforms = with platforms; linux;
+
license = lib.licenses.bsd3;
+
maintainers = with lib.maintainers; [ sargon ];
+
platforms = with lib.platforms; linux;
};
}
+7 -2
pkgs/by-name/sn/snyk/package.nix
···
}:
let
-
version = "1.1295.2";
+
version = "1.1295.3";
in
buildNpmPackage {
pname = "snyk";
···
owner = "snyk";
repo = "cli";
tag = "v${version}";
-
hash = "sha256-cHOIToO9xr+CNS0llwffaTUdhUqFbFcZcrPnBeD+JxE=";
+
hash = "sha256-I7x+HjucTEhiAC6bocMeQO/eAWHYcqwA9FXYvcef3Og=";
};
npmDepsHash = "sha256-RuIavwtTbgo5Ni7oGH2i5VAcVxfS4wKKSX6qHD8CHIw=";
···
postPatch = ''
substituteInPlace package.json \
--replace-fail '"version": "1.0.0-monorepo"' '"version": "${version}"'
+
'';
+
+
postInstall = ''
+
# Remove dangling symlinks created during installation (remove -delete to just see the files, or -print '%l\n' to see the target
+
find -L $out -type l -print -delete
'';
nodejs = nodejs_20;
+3 -3
pkgs/by-name/sy/symfony-cli/package.nix
···
buildGoModule rec {
pname = "symfony-cli";
-
version = "5.10.9";
-
vendorHash = "sha256-UqaRZPCgjiexeeylfP8p0rye6oc+rWac87p8KbVKrdc=";
+
version = "5.11.0";
+
vendorHash = "sha256-6DNirMtVuuWJziDy6HeJxHQnV2f7jmie7kcXvUDfN94=";
src = fetchFromGitHub {
owner = "symfony-cli";
repo = "symfony-cli";
rev = "v${version}";
-
hash = "sha256-Y7wtWrEe1/aftHGdwRA+1Uw9vHJRST9yjhFGYVEgdgo=";
+
hash = "sha256-r8B9lFcTG0TWb3U8eRzg9SkwUY90805wdFlmPbtMywk=";
leaveDotGit = true;
postFetch = ''
git --git-dir $out/.git log -1 --pretty=%cd --date=format:'%Y-%m-%dT%H:%M:%SZ' > $out/SOURCE_DATE
+3 -3
pkgs/by-name/up/upx/package.nix
···
stdenv.mkDerivation (finalAttrs: {
pname = "upx";
-
version = "4.2.4";
+
version = "5.0.0";
src = fetchFromGitHub {
owner = "upx";
repo = "upx";
-
rev = "v${finalAttrs.version}";
+
tag = "v${finalAttrs.version}";
fetchSubmodules = true;
-
hash = "sha256-r36BD5f/sQSz3GjvreOptc7atIaaBZKpU+7qm+BKLss=";
+
hash = "sha256-LWir7Quf53G3Q9Lyw+sokn7Rtes2DxeZD8UsYviy3aA=";
};
nativeBuildInputs = [ cmake ];
+8 -3
pkgs/by-name/vt/vtsls/package.nix
···
nodejs_22,
gitMinimal,
pnpm_8,
+
nix-update-script,
}:
stdenv.mkDerivation (finalAttrs: {
pname = "vtsls";
-
version = "0.2.6";
+
version = "0.2.8";
src = fetchFromGitHub {
owner = "yioneko";
repo = "vtsls";
rev = "server-v${finalAttrs.version}";
-
hash = "sha256-HCi9WLh4IEfhgkQNUVk6IGkQfYagg805Rix78zG6xt0=";
+
hash = "sha256-Ng+aOBnxFRbMjoUy6+DvIk2yVpvJT+AMsbvDb+IlYpY=";
fetchSubmodules = true;
};
···
src
version
;
-
hash = "sha256-4XxQ0Z2atTBItrD9iY7q5rJaCmb1EeDBvQ5+L3ceRXI=";
+
hash = "sha256-xenPpKsIjEIyVeZDjwjLaBbpWLqWQDBaLLfyzxtrsTI=";
};
# Patches to get submodule sha from file instead of 'git submodule status'
···
runHook postInstall
'';
+
+
passthru = {
+
updateScript = nix-update-script { };
+
};
meta = {
description = "LSP wrapper for typescript extension of vscode.";
+38
pkgs/by-name/wa/wasm-language-tools/package.nix
···
+
{
+
lib,
+
rustPlatform,
+
fetchFromGitHub,
+
versionCheckHook,
+
nix-update-script,
+
}:
+
+
rustPlatform.buildRustPackage rec {
+
pname = "wasm-language-tools";
+
version = "0.3.2";
+
+
src = fetchFromGitHub {
+
owner = "g-plane";
+
repo = "wasm-language-tools";
+
tag = "v${version}";
+
hash = "sha256-f1Mq+1gZZelN12rFTLJHOvdzDAbqufzT9+I6pkJdJMU=";
+
};
+
+
useFetchCargoVendor = true;
+
cargoHash = "sha256-P3sxAFZjAlgPrGrw3W+7ufflUz3/Xe7lTXygnSX5Q+4=";
+
+
nativeInstallCheckInputs = [ versionCheckHook ];
+
versionCheckProgram = "${placeholder "out"}/bin/wat_server";
+
versionCheckProgramArg = "--version";
+
doInstallCheck = true;
+
+
passthru.updateScript = nix-update-script { };
+
+
meta = {
+
description = "Language server and other tools for WebAssembly";
+
homepage = "https://github.com/g-plane/wasm-language-tools/";
+
changelog = "https://github.com/g-plane/wasm-language-tools/releases/tag/v${version}/CHANGELOG.md";
+
license = lib.licenses.mit;
+
maintainers = with lib.maintainers; [ ethancedwards8 ];
+
mainProgram = "wat_server";
+
};
+
}
+2 -2
pkgs/development/libraries/python-qt/default.nix
···
stdenv.mkDerivation (finalAttrs: {
pname = "python-qt";
-
version = "3.5.7";
+
version = "3.6.0";
src = fetchFromGitHub {
owner = "MeVisLab";
repo = "pythonqt";
rev = "v${finalAttrs.version}";
-
hash = "sha256-UE0UNp9ArXNFga4MGb80fAQWeq1US5kk7FEZ8tS94OU=";
+
hash = "sha256-TVtnxb8E18x025lZiZLdgCA7ltLtToI1tsmB6gBh2B4=";
};
nativeBuildInputs = [
+6 -32
pkgs/development/ocaml-modules/camlimages/camlimages.patch
···
diff --git a/config/xConfigurator.ml b/config/xConfigurator.ml
-
index 268df4a..73e1850 100644
+
index 766a35c..1d1aff5 100644
--- a/config/xConfigurator.ml
+++ b/config/xConfigurator.ml
@@ -8,7 +8,7 @@ let (!%) fmt = Printf.sprintf fmt
···
let find_program prog =
let prog = prog ^ exe in
@@ -45,13 +45,13 @@ module Configurator = struct
-
| s ->
-
(* findlib 1.7.3 installs META file for graphics
+
| s ->
+
(* findlib 1.7.3 installs META file for graphics
even when there is no graphics library installed. *)
- let dest = Caml.Filename.temp_file "test" ".cma" in
- let res = match Caml.Sys.command & !% "ocamlfind ocamlc -package %s -o %s -linkpkg" n dest with
···
- let fcalls = Caml.List.map (!% " ( (void(*)()) (%s) )();") fnames in
+ let includes = Stdlib.List.map (!% "#include <%s>") headers in
+ let fcalls = Stdlib.List.map (!% " ( (void(*)()) (%s) )();") fnames in
-
let code =
-
String.concat ~sep:"\n"
-
& includes
-
diff --git a/core/images.ml b/core/images.ml
-
index 563ab7e..a53a6a4 100644
-
--- a/core/images.ml
-
+++ b/core/images.ml
-
@@ -102,7 +102,7 @@ let get_extension s =
-
| _ -> s, ""
-
-
let guess_extension s =
-
- let s = String.lowercase s in
-
+ let s = String.lowercase_ascii s in
-
match s with
-
| "gif" -> Gif
-
| "bmp" -> Bmp
-
diff --git a/core/units.ml b/core/units.ml
-
index 634bc9c..ddd6eae 100644
-
--- a/core/units.ml
-
+++ b/core/units.ml
-
@@ -30,7 +30,7 @@ let parse_length s = (* return in pt *)
-
let digit,unit =
-
if l > 2 then String.sub s 0 2, String.sub s (l-2) 2 else "", "" in
-
try
-
- (List.assoc (String.lowercase unit) units) *. float_of_string digit
-
+ (List.assoc (String.lowercase_ascii unit) units) *. float_of_string digit
-
with
-
| Not_found -> (* think it is in "pt" *)
-
float_of_string s in
+
let code =
+
String.concat ~sep:"\n"
+
& includes
+3 -3
pkgs/development/ocaml-modules/camlimages/default.nix
···
buildDunePackage rec {
pname = "camlimages";
-
version = "5.0.4";
+
version = "5.0.5";
minimalOCamlVersion = "4.07";
···
owner = "camlspotter";
repo = pname;
rev = version;
-
sha256 = "1m2c76ghisg73dikz2ifdkrbkgiwa0hcmp21f2fm2rkbf02rq3f4";
+
hash = "sha256-/Dkj8IBVPjGCJCXrLOuJtuaa+nD/a9e8/N+TN9ukw4k=";
};
-
# stdio v0.17 compatibility; also replaces `String.lowercase` with `String.lowercase_ascii`
+
# stdio v0.17 compatibility
patches = [ ./camlimages.patch ];
nativeBuildInputs = [ cppo ];
+28
pkgs/development/ocaml-modules/mirage-ptime/default.nix
···
+
{
+
lib,
+
buildDunePackage,
+
fetchurl,
+
ptime,
+
version ? "5.0.0",
+
}:
+
+
buildDunePackage {
+
inherit version;
+
+
pname = "mirage-ptime";
+
+
src = fetchurl {
+
url = "https://github.com/mirage/mirage-ptime/releases/download/v${version}/mirage-ptime-${version}.tbz";
+
hash = "sha256-1VNWBGjVuU2yWwVzjCSZ8pDuZrFKwitDAuZn8fpENHE=";
+
};
+
+
propagatedBuildInputs = [ ptime ];
+
+
meta = {
+
description = "A POSIX clock for MirageOS";
+
license = lib.licenses.isc;
+
maintainers = [ lib.maintainers.vbgl ];
+
changelog = "https://raw.githubusercontent.com/mirage/mirage-ptime/refs/tags/v${version}/CHANGES.md";
+
homepage = "https://github.com/mirage/mirage-ptime";
+
};
+
}
+3 -3
pkgs/development/ocaml-modules/tls/async.nix
···
async,
cstruct-async,
core,
-
mirage-crypto-rng-async,
+
mirage-crypto-rng,
}:
-
buildDunePackage rec {
+
buildDunePackage {
pname = "tls-async";
inherit (tls) src version;
···
async
core
cstruct-async
-
mirage-crypto-rng-async
+
mirage-crypto-rng
tls
];
+2 -2
pkgs/development/ocaml-modules/tls/default.nix
···
buildDunePackage rec {
pname = "tls";
-
version = "1.0.4";
+
version = "2.0.0";
src = fetchurl {
url = "https://github.com/mirleft/ocaml-tls/releases/download/v${version}/tls-${version}.tbz";
-
hash = "sha256-yFt8Gh4ipseWEHsnJVld3iYElMDvBrYdn1O+IuHcQug=";
+
hash = "sha256-aEcNa6hIAHWQjAzGn/6Cq7y7g6t/mI0mYzWhnxLCamI=";
};
minimalOCamlVersion = "4.08";
+4 -5
pkgs/development/ocaml-modules/tls/eio.nix
···
eio_main,
logs,
mdx,
-
mirage-crypto-rng-eio,
+
mirage-crypto-rng,
ptime,
tls,
}:
-
buildDunePackage rec {
+
buildDunePackage {
pname = "tls-eio";
inherit (tls) src meta version;
minimalOCamlVersion = "5.0";
-
# Tests are not compatible with mirage-crypto-rng 1.2.0
-
doCheck = false;
+
doCheck = true;
nativeCheckInputs = [
mdx.bin
];
···
propagatedBuildInputs = [
ptime
eio
-
mirage-crypto-rng-eio
+
mirage-crypto-rng
tls
];
}
+3 -3
pkgs/development/ocaml-modules/tls/lwt.nix
···
buildDunePackage,
tls,
lwt,
-
mirage-crypto-rng-lwt,
+
mirage-crypto-rng,
}:
-
buildDunePackage rec {
+
buildDunePackage {
pname = "tls-lwt";
inherit (tls) src meta version;
···
propagatedBuildInputs = [
lwt
-
mirage-crypto-rng-lwt
+
mirage-crypto-rng
tls
];
}
+2 -2
pkgs/development/ocaml-modules/tls/mirage.nix
···
tls,
fmt,
lwt,
-
mirage-clock,
mirage-crypto,
mirage-crypto-pk,
mirage-flow,
mirage-kv,
+
mirage-ptime,
ptime,
}:
···
propagatedBuildInputs = [
fmt
lwt
-
mirage-clock
mirage-crypto
mirage-crypto-pk
mirage-flow
mirage-kv
+
mirage-ptime
ptime
tls
];
+2 -2
pkgs/development/php-packages/memprof/default.nix
···
}:
let
-
version = "3.0.2";
+
version = "3.1.0";
in
buildPecl {
inherit version;
···
owner = "arnaud-lb";
repo = "php-memory-profiler";
rev = version;
-
hash = "sha256-K8YcvCobErBkaWFTkVGLXXguQPOLIgQuRGWJF+HAIRA=";
+
hash = "sha256-gq+txAU2Fw+Zm1aIu0lwPUHRqtccNcHFpp0fm3f7BnQ=";
};
configureFlags = [ "--with-judy-dir=${judy}" ];
+2 -2
pkgs/development/python-modules/craft-archives/default.nix
···
buildPythonPackage rec {
pname = "craft-archives";
-
version = "2.0.2";
+
version = "2.1.0";
pyproject = true;
···
owner = "canonical";
repo = "craft-archives";
tag = version;
-
hash = "sha256-1HEz4d1WLQDDHga7X+V/37n8E7JK/k0z+UDeNEiLOHs=";
+
hash = "sha256-VjGoAsmdYyoU7ngU69HVNauEk2/vbcEz2tMCTmjheF4=";
};
postPatch = ''
+2 -2
pkgs/development/python-modules/graph-tool/default.nix
···
in
buildPythonPackage rec {
pname = "graph-tool";
-
version = "2.85";
+
version = "2.91";
format = "other";
src = fetchurl {
url = "https://downloads.skewed.de/graph-tool/graph-tool-${version}.tar.bz2";
-
hash = "sha256-GX0JUz5G7gtLemwlY1prQvCxIxpuyclo+1LN68j2H9o=";
+
hash = "sha256-PIUOkrNe/dce8qvSbZ/lwCEuwqB5kPvnMjQI4Sej/QI=";
};
postPatch = ''
+98 -10
pkgs/development/python-modules/gym/default.nix
···
{
lib,
+
stdenv,
buildPythonPackage,
fetchFromGitHub,
-
numpy,
+
+
# build-system
+
setuptools,
+
+
# dependencies
cloudpickle,
+
numpy,
gym-notices,
importlib-metadata,
pythonOlder,
+
+
# tests
+
moviepy,
+
pybox2d,
+
pygame,
+
pytestCheckHook,
+
opencv-python,
}:
buildPythonPackage rec {
pname = "gym";
version = "0.26.2";
-
format = "setuptools";
+
pyproject = true;
src = fetchFromGitHub {
owner = "openai";
-
repo = pname;
+
repo = "gym";
tag = version;
hash = "sha256-uJgm8l1SxIRC5PV6BIH/ht/1ucGT5UaUhkFMdusejgA=";
};
-
propagatedBuildInputs = [
+
# Fix numpy2 compatibility
+
postPatch = ''
+
substituteInPlace gym/envs/classic_control/acrobot.py \
+
--replace-fail "np.float_" "np.float64"
+
+
substituteInPlace gym/utils/passive_env_checker.py \
+
--replace-fail "np.bool8" "np.bool"
+
+
substituteInPlace tests/envs/test_action_dim_check.py \
+
--replace-fail "np.cast[dtype](OOB_VALUE)" "np.asarray(OOB_VALUE, dtype=dtype)" \
+
--replace-fail "np.alltrue" "np.all"
+
+
substituteInPlace tests/spaces/test_box.py \
+
--replace-fail "np.bool8" "np.bool" \
+
--replace-fail "np.complex_" "np.complex128"
+
+
substituteInPlace tests/wrappers/test_record_episode_statistics.py \
+
--replace-fail "np.alltrue" "np.all"
+
'';
+
+
build-system = [
+
setuptools
+
];
+
+
dependencies = [
cloudpickle
numpy
gym-notices
] ++ lib.optionals (pythonOlder "3.10") [ importlib-metadata ];
-
-
# The test needs MuJoCo that is not free library.
-
doCheck = false;
pythonImportsCheck = [ "gym" ];
-
meta = with lib; {
+
nativeCheckInputs = [
+
moviepy
+
opencv-python
+
pybox2d
+
pygame
+
pytestCheckHook
+
];
+
+
disabledTests =
+
[
+
# TypeError: Converting from sequence to b2Vec2, expected int/float arguments index 0
+
"test_box_actions_out_of_bound"
+
"test_env_determinism_rollout"
+
"test_envs_pass_env_checker"
+
"test_frame_stack"
+
"test_make_autoreset_true"
+
"test_passive_checker_wrapper_warnings"
+
"test_pickle_env"
+
"test_render_modes"
+
+
# TypeError: in method 'b2RevoluteJoint___SetMotorSpeed', argument 2 of type 'float32'
+
"test_box_actions_out_of_bound"
+
+
# TypeError: exceptions must be derived from Warning, not <class 'NoneType'>
+
"test_dict_init"
+
+
# ValueError: setting an array element with a sequence.
+
# The requested array has an inhomogeneous shape after 1 dimensions.
+
# The detected shape was (2,) + inhomogeneous part
+
"test_sample_contains"
+
]
+
++ lib.optionals stdenv.hostPlatform.isDarwin [
+
# Fatal Python error: Aborted
+
# gym/envs/classic_control/cartpole.py", line 227 in render
+
"test_autoclose"
+
"test_call_async_vector_env"
+
"test_call_sync_vector_env"
+
"test_human_rendering"
+
"test_make_render_mode"
+
"test_order_enforcing"
+
"test_record_simple"
+
"test_record_video_reset"
+
"test_record_video_step_trigger"
+
"test_record_video_using_default_trigger"
+
"test_record_video_within_vecto"
+
"test_text_envs"
+
];
+
+
disabledTestPaths = lib.optionals stdenv.hostPlatform.isDarwin [
+
# Fatal Python error: Aborted
+
# gym/utils/play.py", line 62 in __init__
+
"tests/utils/test_play.py"
+
];
+
+
meta = {
description = "Toolkit for developing and comparing your reinforcement learning agents";
homepage = "https://www.gymlibrary.dev/";
-
license = licenses.mit;
-
maintainers = with maintainers; [ hyphon81 ];
+
license = lib.licenses.mit;
+
maintainers = with lib.maintainers; [ GaetanLepage ];
};
}
+3 -3
pkgs/development/python-modules/pylance/default.nix
···
buildPythonPackage rec {
pname = "pylance";
-
version = "0.23.1";
+
version = "0.23.2";
pyproject = true;
src = fetchFromGitHub {
owner = "lancedb";
repo = "lance";
tag = "v${version}";
-
hash = "sha256-H5C4gaXC/Vvzq/5Mr0ZeO3waJJ+v43IWDOVziSb9rgk=";
+
hash = "sha256-HVT/39zhcviVkF6dKM+ZIy7vGW6tOWEBUXTEIuFaVno=";
};
sourceRoot = "${src.name}/python";
···
src
sourceRoot
;
-
hash = "sha256-QSqDxkI4O11s34fL397KPCSBKRieNhzAQu2QZuqD3yE=";
+
hash = "sha256-H5Z6YgaSfRrRPGZVbgNWxcFboS1I65p+08c1VgaL+lE=";
};
nativeBuildInputs = [
+3 -3
pkgs/kde/generated/sources/plasma.json
···
"hash": "sha256-rsrHJm3HkTqxBMaweOWluLpZkRgfDjsRSwnj1/+tOhQ="
},
"kwin": {
-
"version": "6.3.2",
-
"url": "mirror://kde/stable/plasma/6.3.2/kwin-6.3.2.tar.xz",
-
"hash": "sha256-NlWYDV7NgODFnXMwwBBjLB29SFfpw8Mm8uMVIK/qnKM="
+
"version": "6.3.2.1",
+
"url": "mirror://kde/stable/plasma/6.3.2/kwin-6.3.2.1.tar.xz",
+
"hash": "sha256-5hI+2isf2fZbNgYf2s+WI0Bo0eJPp6M1Ioj01RsvxMI="
},
"kwrited": {
"version": "6.3.2",
+1 -1
pkgs/servers/http/apache-httpd/2.4.nix
···
passthru = {
inherit apr aprutil sslSupport proxySupport ldapSupport luaSupport lua5;
tests = {
-
acme-integration = nixosTests.acme;
+
acme-integration = nixosTests.acme.httpd;
proxy = nixosTests.proxy;
php = nixosTests.php.httpd;
cross = runCommand "apacheHttpd-test-cross" { } ''
+1 -1
pkgs/servers/http/nginx/generic.nix
···
nginx-unix-socket
;
variants = lib.recurseIntoAttrs nixosTests.nginx-variants;
-
acme-integration = nixosTests.acme;
+
acme-integration = nixosTests.acme.nginx;
} // passthru.tests;
};
+2 -2
pkgs/servers/x11/xorg/xwayland.nix
···
stdenv.mkDerivation rec {
pname = "xwayland";
-
version = "24.1.5";
+
version = "24.1.6";
src = fetchurl {
url = "mirror://xorg/individual/xserver/${pname}-${version}.tar.xz";
-
hash = "sha256-y0vRcOb6a1RboFZ76PaT0u7M/GLQTGcDfdFPBtqtNh0=";
+
hash = "sha256-c35hLKNrvfQVqRFkTrdZLPk4mEaEe0f6RtxwW9dU0tc=";
};
postPatch = ''
+3
pkgs/tools/audio/liquidsoap/full.nix
···
postPatch = ''
substituteInPlace src/lang/dune \
--replace-warn "(run git rev-parse --short HEAD)" "(run echo -n nixpkgs)"
+
# Compatibility with camlimages 5.0.5
+
substituteInPlace src/core/dune \
+
--replace-warn camlimages.all_formats camlimages.core
'';
dontConfigure = true;
+9 -7
pkgs/tools/inputmethods/ibus-engines/ibus-libpinyin/default.nix
···
opencc,
libsoup_3,
json-glib,
+
libnotify,
}:
stdenv.mkDerivation rec {
pname = "ibus-libpinyin";
-
version = "1.15.8";
+
version = "1.16.0";
src = fetchFromGitHub {
owner = "libpinyin";
repo = "ibus-libpinyin";
-
rev = version;
-
hash = "sha256-u21avBSSu/78tLoyFI9XGocC7rT/64L5HqQQj3Zg1Mc=";
+
tag = version;
+
hash = "sha256-aeyoBfl5x9Jo35vESMmLBbl72Qx5F3JwZHZaBr3c0Jk=";
};
nativeBuildInputs = [
···
opencc
libsoup_3
json-glib
+
libnotify
];
-
meta = with lib; {
+
meta = {
isIbusEngine = true;
description = "IBus interface to the libpinyin input method";
-
license = licenses.gpl3Plus;
-
maintainers = with maintainers; [
+
license = lib.licenses.gpl3Plus;
+
maintainers = with lib.maintainers; [
linsui
ericsagnes
];
-
platforms = platforms.linux;
+
platforms = lib.platforms.linux;
};
}
+11 -13
pkgs/tools/misc/qt6ct/default.nix
···
qttools,
wrapQtAppsHook,
}:
-
let
-
inherit (lib) getDev;
-
in
+
stdenv.mkDerivation (finalAttrs: {
pname = "qt6ct";
-
version = "0.9";
+
version = "0.10";
src = fetchFromGitHub {
-
owner = "trialuser02";
+
owner = "ilya-fedin";
repo = "qt6ct";
-
rev = finalAttrs.version;
-
hash = "sha256-MmN/qPBlsF2mBST+3eYeXaq+7B3b+nTN2hi6CmxrILc=";
+
tag = finalAttrs.version;
+
hash = "sha256-ePY+BEpEcAq11+pUMjQ4XG358x3bXFQWwI1UAi+KmLo=";
};
nativeBuildInputs = [
···
];
qmakeFlags = [
-
"LRELEASE_EXECUTABLE=${getDev qttools}/bin/lrelease"
+
"LRELEASE_EXECUTABLE=${lib.getDev qttools}/bin/lrelease"
"PLUGINDIR=${placeholder "out"}/${qtbase.qtPluginPrefix}"
"LIBDIR=${placeholder "out"}/lib"
];
-
meta = with lib; {
+
meta = {
description = "Qt6 Configuration Tool";
-
homepage = "https://github.com/trialuser02/qt6ct";
-
platforms = platforms.linux;
-
license = licenses.bsd2;
-
maintainers = with maintainers; [
+
homepage = "https://github.com/ilya-fedin/qt6ct";
+
platforms = lib.platforms.linux;
+
license = lib.licenses.bsd2;
+
maintainers = with lib.maintainers; [
Flakebi
Scrumplex
];
+13
pkgs/tools/misc/screen/buffer-overflow-SendCmdMessage.patch
···
+
--- a/attacher.c 2025-02-24 20:15:31.701820351 +0100
+
+++ b/attacher.c 2025-02-24 20:17:05.893826559 +0100
+
@@ -461,8 +461,8 @@
+
size_t len;
+
len = strlen(*av) + 1;
+
if (p + len >= m.m.command.cmd + ARRAY_SIZE(m.m.command.cmd) - 1)
+
- break;
+
+ Panic(0, "Total length of the command to send too large.\n");
+
- strncpy(p, *av, MAXPATHLEN);
+
+ memcpy(p, *av, len);
+
p += len;
+
}
+
*p = 0;
+7
pkgs/tools/misc/screen/default.nix
···
# We need _GNU_SOURCE so that mallocmock_reset() is defined: https://savannah.gnu.org/bugs/?66416
NIX_CFLAGS_COMPILE = lib.optionalString (stdenv.cc.isGNU) "-D_GNU_SOURCE=1 -Wno-int-conversion -Wno-incompatible-pointer-types";
+
patches = [
+
# GNU Screen 5.0 uses strncpy incorrectly in SendCmdMessage
+
# This causes issues detected when using -D_FORTIFY_SOURCE=3
+
# e.g. https://savannah.gnu.org/bugs/index.php?66215
+
./buffer-overflow-SendCmdMessage.patch
+
];
+
nativeBuildInputs = [
autoreconfHook
];
+2
pkgs/top-level/ocaml-packages.nix
···
mirage-protocols = callPackage ../development/ocaml-modules/mirage-protocols { };
+
mirage-ptime = callPackage ../development/ocaml-modules/mirage-ptime { };
+
mirage-random = callPackage ../development/ocaml-modules/mirage-random { };
mirage-random-test = callPackage ../development/ocaml-modules/mirage-random-test { };