Merge remote-tracking branch 'origin/master' into staging-next

This commit is contained in:
K900
2026-05-14 19:08:41 +03:00
78 changed files with 1178 additions and 411 deletions

View File

@@ -1726,11 +1726,21 @@ let
`from`
: 1\. Function argument
: The "from" option path as list of strings.
Option must not exist in the current module set.
`to`
: 2\. Function argument
: The "to" option path as list of strings.
Option must already exist in the current module set.
# Limitations
- The "to" option must already be declared.
- The "from" option should not be declared, as this function will declare it.
- "to" Options whose types don't support merging at any level of their structure (like `types.raw`,
or `types.attrsOf types.raw` where the attribute values can't merge) are not well-supported
because this function wraps aliased definitions in `mkMerge`.
*/
mkRenamedOptionModule =
from: to:

View File

@@ -1,14 +1,24 @@
# Ad-Hoc Configuration {#ad-hoc-network-config}
You can use [](#opt-networking.localCommands) to
specify shell commands to be run at the end of `network-setup.service`. This
is useful for doing network configuration not covered by the existing NixOS
modules. For instance, to statically configure an IPv6 address:
You can use [](#opt-networking.localCommands) to specify shell commands to be
run after the network interfaces have been created, but not necessarily fully
configured.
This is useful for doing network configuration not covered by the existing
NixOS modules. For example, you can create a network namespace and a pair
of virtual ethernet devices like this:
```nix
{
networking.localCommands = ''
ip -6 addr add 2001:610:685:1::1/64 dev eth0
ip netns add mynet
ip link add name veth-in type veth peer name veth-out
ip link set dev veth-out netns mynet
'';
}
```
::: {.note}
The commands should ideally be idempotent, so it's recommended to perform
cleanups of the state you create (e.g. virtual interfaces), or at least make
sure possible failures are handled.
:::

View File

@@ -26,9 +26,16 @@ servers:
```
::: {.note}
Statically configured interfaces are set up by the systemd service
`interface-name-cfg.service`. The default gateway and name server
configuration is performed by `network-setup.service`.
Addresses and routes for statically configured interfaces and the default
gateway are set up by systemd services named
`network-addresses-<interface>.service`. The name servers configuration,
instead, is performed by `network-local-commands.service` using resolvconf.
:::
::: {.note}
If needed, for example if addresses/routes were added/removed,
you can reset the network configuration by running
`systemctl restart networking-scripted.target`
:::
The host name is set using [](#opt-networking.hostName):

View File

@@ -326,6 +326,13 @@ See <https://github.com/NixOS/nixpkgs/issues/481673>.
Note for NetworkManager users: before these changes NetworkManager used to spawn its own wpa_supplicant daemon, but now it relies on `networking.wireless`. So, if you had `networking.wireless.enable = false` in your configuration, you should remove that line.
- Some implementation details of the NixOS network-interfaces module have been changed:
- In the "scripted" backend, `network-setup.service` has been removed and the network configuration services are now part of `network.target`, which is now directly pulled into `multi-user.target`.
- Interface addresses, routes and default gateways are now configured asynchronously as soon as the underlying network devices become available (fixes issue [#154737](https://github.com/NixOS/nixpkgs/issues/154737)).
- In both "networkd" and "scripted" backends, the configuration of name servers is now part of `network-local-commands.service` (fixes issue [#445496](https://github.com/NixOS/nixpkgs/issues/445496)).
- The issue that resulted in a completely unconfigured network if both `resolvconf` was disabled and no default gateway configured, has also been fixed.
- `kratos` has been updated from 1.3.1 to [25.4.0](https://github.com/ory/kratos/releases/tag/v25.4.0). Upstream switched to a new versioning scheme (year.major.minor). Notable breaking changes:
- The `migrate sql` CLI command is now `migrate sql up`

View File

@@ -24,10 +24,12 @@ let
'';
# Sadly, systemd-vconsole-setup doesn't support binary keymaps.
vconsoleConf = pkgs.writeText "vconsole.conf" ''
KEYMAP=${cfg.keyMap}
${lib.optionalString (cfg.font != null) "FONT=${cfg.font}"}
'';
vconsoleConf =
withFont:
pkgs.writeText "vconsole.conf" ''
KEYMAP=${cfg.keyMap}
${lib.optionalString (withFont && cfg.font != null) "FONT=${cfg.font}"}
'';
consoleEnv =
kbd:
@@ -165,7 +167,7 @@ in
# Let systemd-vconsole-setup.service do the work of setting up the
# virtual consoles. Skip when imperative so localectl can manage it.
environment.etc."vconsole.conf" = lib.mkIf (!i18nCfg.imperativeLocale) {
source = vconsoleConf;
source = vconsoleConf true;
};
# Provide kbd with additional packages.
environment.etc.kbd.source = "${consoleEnv pkgs.kbd}/share";
@@ -183,7 +185,7 @@ in
);
boot.initrd.systemd.contents = {
"/etc/vconsole.conf".source = vconsoleConf;
"/etc/vconsole.conf".source = vconsoleConf cfg.earlySetup;
# Add everything if we want full console setup...
"/etc/kbd" = lib.mkIf cfg.earlySetup {
source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share";
@@ -192,9 +194,6 @@ in
"/etc/kbd/keymaps" = lib.mkIf (!cfg.earlySetup) {
source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share/keymaps";
};
"/etc/kbd/consolefonts" = lib.mkIf (!cfg.earlySetup && cfg.font != null) {
source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share/consolefonts";
};
};
boot.initrd.systemd.additionalUpstreamUnits = [
"systemd-vconsole-setup.service"
@@ -204,7 +203,7 @@ in
"${config.boot.initrd.systemd.package.kbd}/bin/setfont"
"${config.boot.initrd.systemd.package.kbd}/bin/loadkeys"
]
++ lib.optionals (cfg.font != null && lib.hasPrefix builtins.storeDir cfg.font) [
++ lib.optionals (cfg.font != null && cfg.earlySetup && lib.hasPrefix builtins.storeDir cfg.font) [
"${cfg.font}"
]
++ lib.optionals (lib.hasPrefix builtins.storeDir cfg.keyMap) [
@@ -226,7 +225,7 @@ in
wantedBy = [ "multi-user.target" ];
restartTriggers =
lib.optionals (!i18nCfg.imperativeLocale) [
vconsoleConf
(config.environment.etc."vconsole.conf".source)
]
++ [
(consoleEnv pkgs.kbd)

View File

@@ -76,7 +76,7 @@ let
{
inputs = {
# This is pointing to an unstable release.
# If you prefer a stable release instead, you can this to the latest number shown here: https://nixos.org/download
# If you prefer a stable release instead, you can change the word unstable to the latest number shown here: https://nixos.org/download
# i.e. nixos-24.11
# Use `nix flake update` to update the flake to the latest revision of the chosen release channel.
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

View File

@@ -119,6 +119,7 @@ let
"smartctl"
"smokeping"
"snmp"
"speedtest"
"sql"
"statsd"
"storagebox"

View File

@@ -0,0 +1,51 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.prometheus.exporters.speedtest;
inherit (lib)
mkOption
types
concatStringsSep
optionalString
;
in
{
port = 9798;
extraOpts = {
serverID = mkOption {
type = types.int;
default = -1;
description = ''
Speedtest.net server ID to run tests against.
-1 picks the closest server to your location.
'';
};
serverFallback = mkOption {
type = types.bool;
default = false;
description = ''
If the configured serverID is unavailable, fall back to the closest available server.
'';
};
};
serviceOpts = {
serviceConfig = {
ExecStart = ''
${pkgs.prometheus-speedtest-exporter}/bin/speedtest_exporter \
-listen-address ${cfg.listenAddress} \
-port ${toString cfg.port} \
-server_id ${toString cfg.serverID} \
${optionalString cfg.serverFallback "-server_fallback"} \
${concatStringsSep " \\\n " cfg.extraFlags}
'';
};
};
}

View File

@@ -8,6 +8,7 @@
let
inherit (lib)
getExe
hasAttr
mapAttrs
match
mkEnableOption
@@ -362,6 +363,15 @@ in
config = mkIf cfg.enable {
environment.systemPackages = [ manage ];
# Some settings options in LaSuite has been renamed in 5.0.0
# Show warnings if those settings are not renamed
# TODO: remove it when the retrocompatibility options will be gone
warnings =
(optional (hasAttr "AI_API_KEY" cfg.settings) "AI_API_KEY has been renamed as OPENAI_SDK_API_KEY in LaSuite Docs")
++ (optional (hasAttr "AI_API_KEY_FILE" cfg.settings) "AI_API_KEY_FILE has been renamed as OPENAI_SDK_API_KEY_FILE in LaSuite Docs")
++ (optional (hasAttr "AI_BASE_URL" cfg.settings) "AI_BASE_URL has been renamed as OPENAI_SDK_BASE_URL in LaSuite Docs");
systemd.services.lasuite-docs-postgresql-setup = mkIf cfg.postgresql.createLocally {
wantedBy = [ "lasuite-docs.target" ];
requiredBy = [ "lasuite-docs.service" ];

View File

@@ -143,6 +143,8 @@ let
"final.target"
"kexec.target"
"systemd-kexec.service"
"soft-reboot.target"
"systemd-soft-reboot.service"
]
++ lib.optional cfg.package.withUtmp "systemd-update-utmp.service"
++ [

View File

@@ -1,7 +1,6 @@
{
config,
lib,
pkgs,
...
}:
@@ -51,6 +50,9 @@
];
boot.initrd.systemd = {
storePaths = lib.mkIf config.system.etc.overlay.mutable [
"${config.system.nixos-init.package}/bin/clear-etc-opaque"
];
mounts = [
{
where = "/run/nixos-etc-metadata";
@@ -131,13 +133,20 @@
before = [ "initrd-fs.target" ];
unitConfig = {
DefaultDependencies = false;
RequiresMountsFor = "/sysroot";
RequiresMountsFor = [
"/sysroot"
# Needed so we can clear stale opaque markers from the
# upperdir based on the contents of the new metadata layer
# before the overlay is mounted.
"/run/nixos-etc-metadata"
];
};
serviceConfig = {
Type = "oneshot";
ExecStart = ''
/bin/mkdir -p -m 0755 /sysroot/.rw-etc/upper /sysroot/.rw-etc/work
'';
ExecStart = [
"/bin/mkdir -p -m 0755 /sysroot/.rw-etc/upper /sysroot/.rw-etc/work"
"${config.system.nixos-init.package}/bin/clear-etc-opaque /run/nixos-etc-metadata /sysroot/.rw-etc/upper"
];
};
};
})

View File

@@ -285,6 +285,13 @@ in
tmpMetadataMount=$(TMPDIR="/run" mktemp --directory -t nixos-etc-metadata.XXXXXXXXXX)
mount --type erofs --options ro,nodev,nosuid ${config.system.build.etcMetadataImage} "$tmpMetadataMount"
${lib.optionalString config.system.etc.overlay.mutable ''
# Clear stale opaque markers from the upperdir so that lowerdir
# entries added by the new generation are not hidden.
# See https://github.com/NixOS/nixpkgs/issues/505475
${config.system.nixos-init.package}/bin/clear-etc-opaque "$tmpMetadataMount" /.rw-etc/upper
''}
# There was no previous /etc mounted. This happens when we're called
# directly without an initrd, like with nixos-enter.
if ! mountpoint -q /etc; then

View File

@@ -51,6 +51,71 @@ let
(lib.concatStringsSep " ")
];
# Converts an IPv4 address literal to a list of bits
parseAddr.ipv4 =
addr:
let
pad = b: lib.replicate (8 - builtins.length b) 0 ++ b;
toBin = n: pad (lib.toBaseDigits 2 (lib.toInt n));
in
lib.concatMap toBin (builtins.splitVersion addr);
# Converts an IPv6 address literal to a list of bits
parseAddr.ipv6 =
addr:
let
pad = b: lib.replicate (16 - builtins.length b) 0 ++ b;
fromHex = n: (builtins.fromTOML "n = 0x${n}").n;
toBin = n: pad (lib.toBaseDigits 2 (fromHex n));
normal = (lib.network.ipv6.fromString addr).address;
in
lib.concatMap toBin (lib.splitString ":" normal);
# Checks if `addr` is part of the `net` subnet
inSubnet =
v: net: addr:
let
prefix = lib.take net.prefixLength (parseAddr.${v} net.address);
match = lib.zipListsWith (a: b: a == b) prefix (parseAddr.${v} addr);
in
lib.all lib.id match;
# Checks if the netmask of all addresses on interface `iface` includes
# the IP address of `gateway`
#
# Note: this is used to check whether networking.defaultGateway relies on
# the given interface, either explicitly, via the `interface` (optional),
# or explicitly, by using an address in a subnet of this interface.
#
# Configuration of the default gateway is then performed as part of that
# interface setup in `configureAddrs`, below.
isGateway =
v: gateway: iface:
lib.any lib.id (
[ (iface.name == gateway.interface) ]
++ map (net: inSubnet v net gateway.address) iface.${v}.addresses
);
# Checks if `gateway` uses an address from `iface` as default source
#
# Note: this is needed to delay the configuration of the gateway and default
# source until the right interfaces and address have been set up, otherwise
# the commands will fail.
hasSource =
v: gateway: iface:
builtins.elem gateway.source (map (i: i.address) iface.${v}.addresses);
# Interfaces corresponding to the default gateways
gateway4Iface = builtins.filter (isGateway "ipv4" cfg.defaultGateway) interfaces;
gateway6Iface = builtins.filter (isGateway "ipv6" cfg.defaultGateway6) interfaces;
# Interfaces corresponding to the default source addresses
#
# Note: the use of `head` here is safe because these expressions
# are evaluated only when `needsSourceIface`, see `configureAddrs` below.
source4Iface = builtins.head (builtins.filter (hasSource "ipv4" cfg.defaultGateway) interfaces);
source6Iface = builtins.head (builtins.filter (hasSource "ipv6" cfg.defaultGateway6) interfaces);
# warn that these attributes are deprecated (2017-2-2)
# Should be removed in the release after next
bondDeprecation = rec {
@@ -118,121 +183,71 @@ let
else
optional (!config.boot.isContainer) (subsystemDevice dev);
hasDefaultGatewaySet =
(cfg.defaultGateway != null && cfg.defaultGateway.address != "")
|| (cfg.enableIPv6 && cfg.defaultGateway6 != null && cfg.defaultGateway6.address != "");
needNetworkSetup =
cfg.resolvconf.enable || cfg.defaultGateway != null || cfg.defaultGateway6 != null;
networkLocalCommands = lib.mkIf needNetworkSetup {
after = [ "network-setup.service" ];
bindsTo = [ "network-setup.service" ];
};
networkSetup = lib.mkIf needNetworkSetup {
description = "Networking Setup";
after = [ "network-pre.target" ];
before = [
"network.target"
"shutdown.target"
];
wants = [ "network.target" ];
# exclude bridges from the partOf relationship to fix container networking bug #47210
partOf = map (i: "network-addresses-${i.name}.service") (
filter (i: !(hasAttr i.name cfg.bridges)) interfaces
);
conflicts = [ "shutdown.target" ];
wantedBy = [ "multi-user.target" ] ++ optional hasDefaultGatewaySet "network-online.target";
unitConfig.ConditionCapability = "CAP_NET_ADMIN";
path = [ pkgs.iproute2 ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
unitConfig.DefaultDependencies = false;
script = ''
${optionalString config.networking.resolvconf.enable ''
# Set the static DNS configuration, if given.
${pkgs.openresolv}/sbin/resolvconf -m 1 -a static <<EOF
${optionalString (cfg.nameservers != [ ] && cfg.domain != null) ''
domain ${cfg.domain}
''}
${optionalString (cfg.search != [ ]) ("search " + concatStringsSep " " cfg.search)}
${flip concatMapStrings cfg.nameservers (ns: ''
nameserver ${ns}
'')}
EOF
''}
# Set the default gateway
${flip concatMapStrings
[
{
version = "-4";
gateway = cfg.defaultGateway;
}
{
version = "-6";
gateway = cfg.defaultGateway6;
}
]
(
{ version, gateway }:
optionalString (gateway != null && gateway.address != "") ''
${optionalString (gateway.interface != null) ''
ip ${version} route replace ${gateway.address} proto static ${
formatIpArgs {
metric = gateway.metric;
dev = gateway.interface;
}
}
''}
ip ${version} route replace default proto static ${
formatIpArgs {
metric = gateway.metric;
via = gateway.address;
window = cfg.defaultGatewayWindowSize;
dev = gateway.interface;
src = gateway.source;
}
}
''
)
}
'';
};
# For each interface <foo>, create a job network-addresses-<foo>.service"
# that performs static address configuration. It has a "wants"
# dependency on <foo>.service, which is supposed to create
# the interface and need not exist (i.e. for hardware
# interfaces). It has a binds-to dependency on the actual
# network device, so it only gets started after the interface
# has appeared, and it's stopped when the interface
# disappears.
# For each interface <foo>, creates a network-addresses-<foo>.service
# job that performs static address configuration.
#
# It has a Wants dependency on <foo>-netdev.service, which creates
# create the interface, or on a device unit (for hardware interfaces).
# It also has a BindsTo dependency on the device unit: so, it only gets
# started after the interface has appeared and it's stopped when the
# interface disappears.
#
# Unless in a container, the job is not made part of network.target, so
# if an interface is not found (e.g. a USB interface not plugged in) it
# will not hang the boot sequence.
#
# If the interface is the default gateway, the job will also set the
# default gateway and delay network-online.target.
configureAddrs =
i:
let
ips = interfaceIps i;
isDefaultGateway4 = cfg.defaultGateway != null && builtins.elem i gateway4Iface;
isDefaultGateway6 = cfg.defaultGateway6 != null && builtins.elem i gateway6Iface;
needsSourceIface4 =
isDefaultGateway4 && cfg.defaultGateway.source != null && i.name != source4Iface.name;
needsSourceIface6 =
isDefaultGateway6 && cfg.defaultGateway6.source != null && i.name != source6Iface.name;
configureGateway =
version: gateway:
optionalString (gateway.address != "") ''
echo -n "setting ${i.name} as default IPv${version} gateway... "
${optionalString (gateway.interface != null) ''
ip -${version} route replace ${gateway.address} proto static ${
formatIpArgs {
metric = gateway.metric;
dev = gateway.interface;
}
}
''}
ip -${version} route replace default proto static ${
formatIpArgs {
metric = gateway.metric;
via = gateway.address;
window = cfg.defaultGatewayWindowSize;
dev = gateway.interface;
src = gateway.source;
}
}
echo "done"
'';
in
nameValuePair "network-addresses-${i.name}" {
description = "Address configuration of ${i.name}";
wantedBy = [
"network-setup.service"
"network.target"
];
# order before network-setup because the routes that are configured
# there may need ip addresses configured
before = [ "network-setup.service" ];
wantedBy =
deviceDependency i.name
++ optional config.boot.isContainer "network.target"
++ optional (isDefaultGateway4 || isDefaultGateway6) "network-online.target";
bindsTo = deviceDependency i.name;
after = [ "network-pre.target" ] ++ (deviceDependency i.name);
partOf = [ "networking-scripted.target" ];
after = [
"network-pre.target"
]
++ optional needsSourceIface4 "network-addresses-${source4Iface.name}.service"
++ optional needsSourceIface6 "network-addresses-${source6Iface.name}.service"
++ deviceDependency i.name;
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
# Restart rather than stop+start this unit to prevent the
@@ -284,6 +299,10 @@ let
fi
''
)}
# Set the default gateway
${optionalString isDefaultGateway4 (configureGateway "4" cfg.defaultGateway)}
${optionalString isDefaultGateway6 (configureGateway "6" cfg.defaultGateway6)}
'';
preStop = ''
state="/run/nixos/network/routes/${i.name}"
@@ -311,13 +330,13 @@ let
nameValuePair "${i.name}-netdev" {
description = "Virtual Network Interface ${i.name}";
bindsTo = optional (!config.boot.isContainer) "dev-net-tun.device";
partOf = [ "networking-scripted.target" ];
after = optional (!config.boot.isContainer) "dev-net-tun.device" ++ [ "network-pre.target" ];
wantedBy = [
"network.target"
"network-setup.service"
(subsystemDevice i.name)
];
before = [ "network-setup.service" ];
before = [ "network.target" ];
path = [ pkgs.iproute2 ];
serviceConfig = {
Type = "oneshot";
@@ -343,18 +362,21 @@ let
description = "Bridge Interface ${n}";
wantedBy = [
"network.target"
"network-setup.service"
(subsystemDevice n)
];
bindsTo = deps ++ optional v.rstp "mstpd.service";
partOf = [ "network-setup.service" ] ++ optional v.rstp "mstpd.service";
partOf = [
"network.target"
"networking-scripted.target"
]
++ optional v.rstp "mstpd.service";
after = [
"network-pre.target"
]
++ deps
++ optional v.rstp "mstpd.service"
++ map (i: "network-addresses-${i}.service") v.interfaces;
before = [ "network-setup.service" ];
before = [ "network.target" ];
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
path = [ pkgs.iproute2 ];
@@ -448,15 +470,14 @@ let
description = "Open vSwitch Interface ${n}";
wantedBy = [
"network.target"
"network-setup.service"
(subsystemDevice n)
]
++ internalConfigs;
# before = [ "network-setup.service" ];
# should work without internalConfigs dependencies because address/link configuration depends
# on the device, which is created by ovs-vswitchd with type=internal, but it does not...
before = [ "network-setup.service" ] ++ internalConfigs;
partOf = [ "network-setup.service" ]; # shutdown the bridge when network is shutdown
before = [ "network.target" ] ++ internalConfigs;
partOf = [
"network.target"
"networking-scripted.target"
]; # shutdown the bridge when network is shutdown
bindsTo = [ "ovs-vswitchd.service" ]; # requires ovs-vswitchd to be alive at all times
after = [
"network-pre.target"
@@ -521,12 +542,12 @@ let
description = "Bond Interface ${n}";
wantedBy = [
"network.target"
"network-setup.service"
(subsystemDevice n)
];
bindsTo = deps;
partOf = [ "networking-scripted.target" ];
after = [ "network-pre.target" ] ++ deps ++ map (i: "network-addresses-${i}.service") v.interfaces;
before = [ "network-setup.service" ];
before = [ "network.target" ];
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
path = [
@@ -570,12 +591,12 @@ let
description = "MACVLAN Interface ${n}";
wantedBy = [
"network.target"
"network-setup.service"
(subsystemDevice n)
];
bindsTo = deps;
partOf = [ "networking-scripted.target" ];
after = [ "network-pre.target" ] ++ deps;
before = [ "network-setup.service" ];
before = [ "network.target" ];
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
path = [ pkgs.iproute2 ];
@@ -602,12 +623,12 @@ let
description = "IPVLAN Interface ${n}";
wantedBy = [
"network.target"
"network-setup.service"
(subsystemDevice n)
];
bindsTo = deps;
partOf = [ "networking-scripted.target" ];
after = [ "network-pre.target" ] ++ deps;
before = [ "network-setup.service" ];
before = [ "network.target" ];
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
path = [ pkgs.iproute2 ];
@@ -647,12 +668,12 @@ let
description = "FOU endpoint ${n}";
wantedBy = [
"network.target"
"network-setup.service"
(subsystemDevice n)
];
bindsTo = deps;
partOf = [ "networking-scripted.target" ];
after = [ "network-pre.target" ] ++ deps;
before = [ "network-setup.service" ];
before = [ "network.target" ];
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
path = [ pkgs.iproute2 ];
@@ -677,12 +698,11 @@ let
description = "IPv6 in IPv4 Tunnel Interface ${n}";
wantedBy = [
"network.target"
"network-setup.service"
(subsystemDevice n)
];
bindsTo = deps;
after = [ "network-pre.target" ] ++ deps;
before = [ "network-setup.service" ];
before = [ "network.target" ];
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
path = [ pkgs.iproute2 ];
@@ -720,12 +740,12 @@ let
description = "IP in IP Tunnel Interface ${n}";
wantedBy = [
"network.target"
"network-setup.service"
(subsystemDevice n)
];
bindsTo = deps;
partOf = [ "networking-scripted.target" ];
after = [ "network-pre.target" ] ++ deps;
before = [ "network-setup.service" ];
before = [ "network.target" ];
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
path = [ pkgs.iproute2 ];
@@ -768,12 +788,12 @@ let
description = "GRE Tunnel Interface ${n}";
wantedBy = [
"network.target"
"network-setup.service"
(subsystemDevice n)
];
bindsTo = deps;
partOf = [ "networking-scripted.target" ];
after = [ "network-pre.target" ] ++ deps;
before = [ "network-setup.service" ];
before = [ "network.target" ];
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
path = [ pkgs.iproute2 ];
@@ -803,13 +823,15 @@ let
description = "VLAN Interface ${n}";
wantedBy = [
"network.target"
"network-setup.service"
(subsystemDevice n)
];
bindsTo = deps;
partOf = [ "network-setup.service" ];
partOf = [
"network.target"
"networking-scripted.target"
];
after = [ "network-pre.target" ] ++ deps;
before = [ "network-setup.service" ];
before = [ "network.target" ];
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
path = [ pkgs.iproute2 ];
@@ -845,13 +867,39 @@ let
// mapAttrs' createGreDevice cfg.greTunnels
// mapAttrs' createVlanDevice cfg.vlans
// {
network-setup = networkSetup;
network-local-commands = networkLocalCommands;
network-local-commands = {
after = [ "network-pre.target" ];
wantedBy = [ "network.target" ];
};
};
services.udev.extraRules = ''
KERNEL=="tun", TAG+="systemd"
'';
# Note: the scripted networking backend consistent of many
# independent services that are linked to the network.target.
# Since there is no daemon (e.g systemd-networkd) that is
# started as part of the system and pulls in network.target.
# Thus, to start these services we link network.target directly
# to multi-user.target, this has the same result.
systemd.targets.network.wantedBy = [ "multi-user.target" ];
# This target serves no purpose during the boot, but can be
# used to quickly reset the network configuration by running
# systemctl restart networking-scripted.target
systemd.targets.networking-scripted = {
description = "NixOS scripted networking setup";
};
services.udev.extraRules = lib.concatStringsSep "\n" (
[ ''KERNEL=="tun", TAG+="systemd"'' ]
# This creates a udev rule to start each service with a WantedBy
# dependency on a device unit. It's needed because if the service
# unit is loaded in stage 2 but its device was already up by
# stage 1, systemd will not automatically start it.
++ lib.forEach (lib.attrNames cfg.interfaces) (
iface:
''ACTION=="add", SUBSYSTEM=="net", KERNEL=="${iface}", ''
+ ''ENV{SYSTEMD_WANTS}="network-addresses-${iface}.service"''
)
);
};

View File

@@ -747,10 +747,9 @@ in
default = "";
example = "text=anything; echo You can put $text here.";
description = ''
Shell commands to be executed at the end of the
`network-setup` systemd service. Note that if
you are using DHCP to obtain the network configuration,
interfaces may not be fully configured yet.
Shell commands to be executed after all the network
interfaces have been created, but not necessarily
fully configured.
'';
};
@@ -1853,6 +1852,20 @@ in
'';
};
};
networking.localCommands = lib.mkIf config.networking.resolvconf.enable ''
# Set the static DNS configuration, if given.
${pkgs.openresolv}/sbin/resolvconf -m 1 -a static <<EOF
${optionalString (cfg.nameservers != [ ] && cfg.domain != null) ''
domain ${cfg.domain}
''}
${optionalString (cfg.search != [ ]) ("search " + concatStringsSep " " cfg.search)}
${flip concatMapStrings cfg.nameservers (ns: ''
nameserver ${ns}
'')}
EOF
'';
services.mstpd = mkIf needsMstpd { enable = true; };
virtualisation.vswitch = mkIf (cfg.vswitches != { }) { enable = true; };

View File

@@ -27,6 +27,12 @@
specialisation.new-generation.configuration = {
environment.etc."newgen".text = "newgen";
# Regression test for https://github.com/NixOS/nixpkgs/issues/505475:
# A symlink in a subdirectory that does not exist in the base generation's
# lowerdir. If something creates that subdirectory at runtime before
# switching (e.g. stage-2-init.sh creating /etc/nixos), overlayfs makes it
# opaque, hiding lowerdir content added by the new generation.
environment.etc."nixos/newlink".source = pkgs.emptyDirectory;
};
specialisation.newer-generation.configuration = {
environment.etc."newergen".text = "newergen";
@@ -53,6 +59,13 @@
machine.succeed("stat --format '%F' /etc/modetest2 | tee /dev/stderr | grep -Eq '^regular file$'")
machine.succeed("stat --format '%a' /etc/modetest2 | tee /dev/stderr | grep -Eq '^300$'")
with subtest("/etc/nixos created by stage-2-init is opaque in upperdir"):
# stage-2-init.sh unconditionally runs `install -d /etc/nixos`. Since
# /nixos is not in the lowerdir, overlayfs creates it as an opaque dir
# in the upperdir. Verify this precondition for the regression test below.
machine.succeed("test -d /.rw-etc/upper/nixos")
print(machine.succeed("getfattr -h -d -m 'trusted.overlay' /.rw-etc/upper/nixos 2>&1 || true"))
with subtest("switching to the same generation"):
machine.succeed("/run/current-system/bin/switch-to-configuration test")
@@ -77,6 +90,15 @@
assert machine.succeed("cat /etc/newgen") == "newgen"
assert machine.succeed("cat /etc/mutable") == "mutable"
# Regression test for https://github.com/NixOS/nixpkgs/issues/505475:
# The opaque /etc/nixos in the upperdir (created by stage-2-init.sh
# before /nixos existed in the lowerdir) must not hide lowerdir entries
# added by the new generation. The activation script must have cleared
# the stale opaque marker.
print(machine.succeed("ls -la /etc/nixos/"))
machine.succeed("test -L /etc/nixos/newlink")
machine.fail("getfattr -h -n trusted.overlay.opaque /.rw-etc/upper/nixos")
print(machine.succeed("findmnt /etc/mountpoint"))
print(machine.succeed("stat /etc/mountpoint/extra-file"))
print(machine.succeed("findmnt /etc/filemount"))
@@ -93,5 +115,23 @@
numOfMetaMounts = len(metaMounts.splitlines())
assert numOfTmpMounts == 0, f"Found {numOfTmpMounts} remaining tmpmounts"
assert numOfMetaMounts == 1, f"Found {numOfMetaMounts} remaining metamounts"
with subtest("stale opaque markers are cleared by initrd on boot (NixOS/nixpkgs#505475)"):
# Simulate the bug precondition: an opaque /pam.d in the upperdir.
# /pam.d is guaranteed to exist as a directory in the metadata layer.
machine.succeed("mkdir -p /.rw-etc/upper/pam.d")
machine.succeed("setfattr -h -n trusted.overlay.opaque -v y /.rw-etc/upper/pam.d")
machine.succeed("getfattr -h -n trusted.overlay.opaque /.rw-etc/upper/pam.d")
# Also create a non-opaque upperdir directory that exists in the
# metadata layer, to ensure clear-etc-opaque tolerates the
# already-clear case.
machine.succeed("mkdir -p /.rw-etc/upper/systemd")
# Reboot and verify the initrd rw-etc service cleared the opaque marker.
machine.shutdown()
machine.start()
machine.wait_for_unit("multi-user.target")
machine.fail("getfattr -h -n trusted.overlay.opaque /.rw-etc/upper/pam.d")
machine.succeed("test -e /etc/pam.d/login")
'';
}

View File

@@ -44,17 +44,13 @@ let
defaultGateway6 = {
address = "fd00:1234:5678:1::1";
interface = "enp1s0";
source = "fd00:1234:5678:1::3";
source = "fd00:1234:5678:1::3"; # implicit dependency on enp2s0
};
interfaces.enp1s0.ipv6.addresses = [
{
address = "fd00:1234:5678:1::2";
prefixLength = 64;
}
{
address = "fd00:1234:5678:1::3";
prefixLength = 128;
}
];
interfaces.enp1s0.ipv4.addresses = [
{
@@ -76,6 +72,12 @@ let
prefixLength = 24;
}
];
interfaces.enp2s0.ipv6.addresses = [
{
address = "fd00:1234:5678:1::3";
prefixLength = 128;
}
];
};
};
testScript = ''
@@ -108,6 +110,41 @@ let
client.succeed("ip -6 route show default | grep -q 'src fd00:1234:5678:1::3'")
'';
};
dynamicInterface = {
name = "dynamicInterface";
nodes.machine = clientConfig {
networking.interfaces.usb0 = {
ipv6.addresses = lib.singleton {
address = "fd::1";
prefixLength = 127;
};
};
networking.defaultGateway6 = {
address = "fd::";
interface = "usb0";
source = "fd::1";
};
};
testScript = ''
with subtest("Network comes up without usb0"):
machine.wait_for_unit("network.target")
with subtest("multi-user.target does not hang"):
machine.require_unit_state("multi-user.target", "active")
with subtest("usb0 is configured when plugged in"):
machine.succeed("ip link add usb0 type sit local 1.2.3.4")
machine.wait_until_succeeds("ip addr show dev usb0 | grep -q fd::1")
with subtest("Network is now online"):
machine.systemctl("start network-online.target")
machine.require_unit_state("network-online.target", "active")
with subtest("Default gateway is now set"):
machine.succeed("ip -6 route show default | grep -q 'via fd::'")
machine.succeed("ip -6 route show default | grep -q 'src fd::1'")
'';
};
routeType = {
name = "RouteType";
nodes.client = clientConfig {

View File

@@ -65,50 +65,50 @@
"@node-rs/jieba-win32-arm64-msvc@npm:1.10.4": "68146cf2b5324c461847da9fd36aa851aeb5655f94b0178a194630c80926e914b71f5fe60a6853c9197516e2b2680303bf28c405963f8655d0099d335c5178bd",
"@node-rs/jieba-win32-ia32-msvc@npm:1.10.4": "38deadbf93c6ffb410a880f8938034635e2ca755b956b7cf0b751f287dbfb4b5e58a492f532f23b30e77a7b3889467055c4d9be4d76df8c4416d97f96c242f93",
"@node-rs/jieba-win32-x64-msvc@npm:1.10.4": "073b499e19d211f416a604a791c7a29c99552b328d0565d8381258a49682f87014274eebdaafcc9477238a2fd555b0e18fedccb45a49715ad3332958c6f43905",
"@oxfmt/binding-android-arm-eabi@npm:0.32.0": "edad7a793a30eef9d34b2d904fc50a849e8aecfcd8e2c2246cadb6fce8ae14c371100f1fa636c5b66b9118949259eefb968df4391c4047c3a92e678696697b64",
"@oxfmt/binding-android-arm64@npm:0.32.0": "e1c3eb67b295627353f0ab9874e53aca9539a35f8157fbbfc6021ccee4d2761bf27db7905b106afe30a646af9e2c802c226f4a58c85b3431df88d352d02a4e2d",
"@oxfmt/binding-darwin-arm64@npm:0.32.0": "0c4a98fb78f9984cda0f74f7f64145597beca8ba2916333bd9e6f4aaf49fdbe5f5cc99fe2aff4a202c7e64d640251997c3ed9d64eefa1b51382f92c47518723a",
"@oxfmt/binding-darwin-x64@npm:0.32.0": "b97916dd2fe96a09b387f3e950efcce3f4f9b1302c7efa5e7a9b587154929d072a7105aca9d6e4e140ab30b4901d803b1ba5657d0181cbe12a75b92d94400b42",
"@oxfmt/binding-freebsd-x64@npm:0.32.0": "5c0eb0d0e7911ee3f6e062c0bc122b92a09aba4d4d55236df331ca24eeb3e90848064c45570a44bbb1fb2cbafc0aab06ea4f85d2c07b68ff4fc1d2bac60d21a2",
"@oxfmt/binding-linux-arm-gnueabihf@npm:0.32.0": "3e76615d5369cdc639983209994c7ede0fbc5aa2e05674cf94f5bd325e290fb772f98ad7576da30bb744634e14c82bd7ba03a3715e1977c87c0204918ea7e18d",
"@oxfmt/binding-linux-arm-musleabihf@npm:0.32.0": "a4de9deb92d9582a6bce41316d14d9a0867812db561f8514e1f88d6783bb86d396cf39289c203cf1d840636e65687b8fab40ee3823459fae23579778743dc7a9",
"@oxfmt/binding-linux-arm64-gnu@npm:0.32.0": "c8d7d6011b3ea535399d40790ac89ec04211dc54c066f2bd624c131df51653be47f8ddfe499bdaebeb1ef655cf72a09ebbd58446f376eae6153ed45c8513dbad",
"@oxfmt/binding-linux-arm64-musl@npm:0.32.0": "a6d4281feb3babe222ca0c12cb837ee3f836e6c36f25f036376e8df7976c0ecd46588e202f4553d9858ceb44c277604803870ae3847cca697eef883dd16e878e",
"@oxfmt/binding-linux-ppc64-gnu@npm:0.32.0": "69d40a7e7ffe699e07d22b8581ef4f9152075a88f8ee87f83c02aea5d8666c3445d4b121125b2db0b05d533447f8f8b15a86ce10151bfb4d5a48e9ed985cd514",
"@oxfmt/binding-linux-riscv64-gnu@npm:0.32.0": "352e28463804f4f5774a7c6375314c8f66e910945da060e7129ea6f9ef4e2b05172f398825279c008ecd2df278fdf25ffe81ceb2c42c7be745dad1e8caea7176",
"@oxfmt/binding-linux-riscv64-musl@npm:0.32.0": "e108350c2ebbadd6e0c7e7193f87ec816c3175478c17d63b8eebbb8901dff5c73d14265beff33b9613fc17ffa62956831f57d74eb326defed61c9238386c99d7",
"@oxfmt/binding-linux-s390x-gnu@npm:0.32.0": "6cf9b827c188e2d2676d3e52ec5c80d07c1ec1dee8d2b8f21784e70b2f881efdc1b0566d6ff611104b62d83e880b91e253cea6f6154052a724f70e5bbe41e1b9",
"@oxfmt/binding-linux-x64-gnu@npm:0.32.0": "65308d09753a0390eb1485297d853c943b2c28fadcb11f8e566fbeb74e58ea7b977f996eb14f2f839ae58bddc1fd9fc2562dc21807aa4a50f86be5ed5f506435",
"@oxfmt/binding-linux-x64-musl@npm:0.32.0": "13fe9b607e30cce76e9600ac510035b27f387d57cb3424927853f74af1467580c5bfee6021774044ae12b817985a49793711d6f9f10eb368d762d7bc18ae709d",
"@oxfmt/binding-openharmony-arm64@npm:0.32.0": "5df994c65584856841c72097fd9bdba825446cc37b36538655f440af61511bd9a7fc54691e5fa67af116f16cfc88dfe25c2d3cd292c098165cde4e598e96ca71",
"@oxfmt/binding-win32-arm64-msvc@npm:0.32.0": "425871b575111f7738404b70d927ec99d5cea4486780371f9b7fb08165afd26988cbfd9248d6daea6a922bcfd8cbe16caa6374fdfa9d3b62b317f5cd565f83ef",
"@oxfmt/binding-win32-ia32-msvc@npm:0.32.0": "4cd056aa20632b2b40bb29d31e6f3f29b7645113339d514bcbe67fcf532ee474e357b851366177d4e333e6e9cce8008099b7c81d2098b5c2c3dc3b1c3bef7746",
"@oxfmt/binding-win32-x64-msvc@npm:0.32.0": "177af22b383015f3e4ebb46ae308e21c28df9a157b098533c45039de96008ab05b0f91f0148c159b4d5f6ca1627b8e600d71c2ce800826bda5961b9e23b4eb95",
"@oxlint-tsgolint/darwin-arm64@npm:0.13.0": "452933ef927377bf5a8339abae8480168838dcf43dbac185138dced6bcb182a82415e45bceb60176cefed270f3a6f0b25b82c51407ce47b5affa33c147b9e6e3",
"@oxlint-tsgolint/darwin-x64@npm:0.13.0": "7572222e4cc5c3fae9ba51f86f30542d9581b6e9493ebbcb54842f5321a34972e7d6d7e6cf802a096d17bafa983e000fd6a72025897e3181b64a3e5fb4440802",
"@oxlint-tsgolint/linux-arm64@npm:0.13.0": "94cc4283f9c9857778d68030ce4e2e337b258dea153e71fe4d3505b4b4c996d8c69d18c1969953ef5030e64ff2aabb49811ba5a03069d95ed9f880a22502118e",
"@oxlint-tsgolint/linux-x64@npm:0.13.0": "c2855e98f9bcf2ecc893c06518ab23dbcb661ba4c18bca60e6a48d12ed3a52737b23fed5ebe30f0c6afd4df9538a9c34330e15e42de4efb120b7eb0ebfeeba2a",
"@oxlint-tsgolint/win32-arm64@npm:0.13.0": "e46e11b10c2d2e8efdbe110e99153036e34d1f1e19d0eb111c8717602228d3bfcee4aea04f2a1d2510aa6884fdf4c47f3a533782e45a6872721cb828f5065ab9",
"@oxlint-tsgolint/win32-x64@npm:0.13.0": "d200689f40a6bc805b079230f1a731dae3b87d0747ea197ffcfaed94b2131335941c71b2e2df64df917ecf14f13cee4892195727c5fe996909d81edff499964f",
"@oxlint/binding-android-arm-eabi@npm:1.56.0": "463bd488374f85f064d5180bbf21ce8b265d6d9784e7f63af2195d35247d545ccb356ecfa1c16ee6ab31aa187f520d348b7198f49d1d6c546c49d25fe7f7e65a",
"@oxlint/binding-android-arm64@npm:1.56.0": "fabf10dec868c468d57fa9f291ba140a006cd8f6a03cf959e3961d072bfc776f9486b8bb4141b7ea3a31221b9a92299386b1cbb2a2e547fa66f2006b4be86ed9",
"@oxlint/binding-darwin-arm64@npm:1.56.0": "3041dd4d4d22c59107e9694b33b7b87ca2f62f6a0bb8880812986cd795709594fd02af791f6f7c87d4433198ef772b945915a50ab2d7984b4abca1128b8ec671",
"@oxlint/binding-darwin-x64@npm:1.56.0": "55ea3a1aca8a9dec9c91dde13e8c8a2de48256a1a548f7237bc150e12b6b861719f5d663fb0934096bc0491c5669b8628fbc68407dce01acdb49b5a505852c63",
"@oxlint/binding-freebsd-x64@npm:1.56.0": "f76e1637d00c3aca4371c9f469c133bb3290a13a2fe51e3e9e18c6e10de17fb10477bc73d77ce43053172e9314d2662adbc9db8ac51a51d0f8f7d78d3a651306",
"@oxlint/binding-linux-arm-gnueabihf@npm:1.56.0": "e385462613e6e5e479575c860dcd0855db092a163f0498ba9b89dd81f691704b9bc4b1818c0dc6dcc3973ad1fff1d2710797f87304ad3cfd9da6280766d7d520",
"@oxlint/binding-linux-arm-musleabihf@npm:1.56.0": "35f796b2ec0e4bd8f086e664033c1fd4c338b392ea8b9ab96821e00c75d05e0d3aacc2f270eb03e70a00013c52e1d53578df728065c48c9193e323aabd1bd0cd",
"@oxlint/binding-linux-arm64-gnu@npm:1.56.0": "6216ff386bb4e4fbcbb53fa69603f7e9eb1a156c0d3a34aca8cf664d8ff21a17804a905f6917626581037fc8b1a4f962302d626dee15160983c3e35960157359",
"@oxlint/binding-linux-arm64-musl@npm:1.56.0": "4e1e8fbbe11e39549e9c1def3ce7fdcd257b5eca46458bf9c82e3a5971775617dca7c6e3a50fe2c09c69b71980fee7fdd281ed1372af25a49b3497a7effe6050",
"@oxlint/binding-linux-ppc64-gnu@npm:1.56.0": "0688cf639b9d51c1e87c9aabcf9005cec6bfbf4d939871dc2853711935c3788bccec1a8aa3bf38af5a7a8821abca3fa68c5600424d34ebc339ab518a6b18bc21",
"@oxlint/binding-linux-riscv64-gnu@npm:1.56.0": "8a5eb4ad619be00b4345144c2af924f91ca311578386f96f0954f1d5cb64442e8dea857aa91fda55279a8f24f58b6f99d9824646976bc35c556da406218467b8",
"@oxlint/binding-linux-riscv64-musl@npm:1.56.0": "8e4b81e2fd1781b2017528973b37db4c46fee35a1dc8b317295ee65652e55bf8238b6560d6d48a4bd0a541b9f67730c605082842768eab67b4b06158503241c0",
"@oxlint/binding-linux-s390x-gnu@npm:1.56.0": "9f3c598dede43a697fa53014145c895aea1e1f5390d7c5e28bd0a9a03dd32536287373a98d1ce372a41c5be1141785aef19dc63432aeddc1a86f220814cfc183",
"@oxlint/binding-linux-x64-gnu@npm:1.56.0": "8cab5595ee3e7d39a3ea0add9813efbe57139e445b754f5dbbbfade372ef85c8784f5bd2cb0860941a1b7e9173f4b782ae20c8ecd4b59c4dab9ba80de3382aa0",
"@oxlint/binding-linux-x64-musl@npm:1.56.0": "b7111435cc3938911aa5fc05e90a8b878f0b848e7de4cbbd2f4cd633ceeca785b6b0354dd5fd37174485694ca333292039a142bbde0e546e4d8a58ba62d296f2",
"@oxlint/binding-openharmony-arm64@npm:1.56.0": "8d7184c5517c185f64ab6b076a9e59277bf49d46cff8e79f708992bf52d9b6fec9c83e04f7c94d8527044051709eb55e5b82e304e9af9e6cef93f66894601569",
"@oxlint/binding-win32-arm64-msvc@npm:1.56.0": "7fb42e1b53f453c53b33ea2f6564e864bb8245669e6dc52c0d4f9a27badeb52e7764c34b6f64b4a87ab056dd636b608be161aca5f0cbe29613365c60ff98810a",
"@oxlint/binding-win32-ia32-msvc@npm:1.56.0": "fc397d81eb0c0e319ec3e3242ce1f05efe8368e659c6aeba99e630a303b539a80adfe97bc361430b680d24ae713a163712d087205ac8c60804ca77c8018c2a1e",
"@oxlint/binding-win32-x64-msvc@npm:1.56.0": "29693e27fceb5daba0d13a1daa9f6767b2462a7e91ab8107164bdfebf6ccd88a8f648768c2cc024faa76dd75b22f54be16cdd50146e4483b9b8da2402d38d7a1",
"@oxfmt/binding-android-arm-eabi@npm:0.44.0": "f2b29210336a7a06da7b20dd7b793b3e5f0fb1785af1deb7ecce7a8360a694d20998cec5cdfa6ef97cf6d3ed964f7771c32ace3c762b091e4ff716663d153f40",
"@oxfmt/binding-android-arm64@npm:0.44.0": "08581f63c72281f9abb02c8414803eb352c2b0b59d6a16ad525ae14c1f94cadec7986cfa3f1caf1f1ef6f35811c18f070b409fb087878c413d740d4da271373b",
"@oxfmt/binding-darwin-arm64@npm:0.44.0": "e77c7798f60ac34847d861e7e26973c434d00de3ba8bcc789a9658bd55173dce42fbe2db7fa33535f77a6f3d806f95ed18a177ca1fd24035298aeec7d76ae5c0",
"@oxfmt/binding-darwin-x64@npm:0.44.0": "8f2932eaf568c807e9f0a1b246eb43fa84d17ec904423b44b07f494bd7dd2fb77a467d2e6a983d9bcaa072950b52b9bc34d4d1a91491514251532edee0873b0b",
"@oxfmt/binding-freebsd-x64@npm:0.44.0": "2554102821657a66f606cee2ddd283d14a0de691fb016817221ced0be39fc5af7dfe23886ea0bb4079cab48aa56beb4be7d34d38863518c48eef6fd0c64a315d",
"@oxfmt/binding-linux-arm-gnueabihf@npm:0.44.0": "cd24da3e57c6757b07ed5e13d3e3293790ecc1c2f198356d16f3cf58b221ec7901417c275a51cf9464faf533aa6a99aaa1b9ff845f2ceb71699e6939c49b2d90",
"@oxfmt/binding-linux-arm-musleabihf@npm:0.44.0": "5980bd7834eb040c5e0e7551d0032e17c73d446253af017c7f7053a0672d51f0346d03b6530979c6f6f6f8564cde6a11ac329c48338c16bee73d188bb7692649",
"@oxfmt/binding-linux-arm64-gnu@npm:0.44.0": "0b79af84e92ce7200d524e04e48e59276580552763a62b05d247066f0a788729db94fd985c81b92348d794a5f654e686a17542b228b4a5ac0776c30a786abafa",
"@oxfmt/binding-linux-arm64-musl@npm:0.44.0": "1a281d213b7f02f6e28519b2ce7a4bd5c9b540c0c37f2c3b9b786cd616fa91e134b5a17d3da593f311e7b272d5534fa913af40829463987ddf2dc70fcda63cfd",
"@oxfmt/binding-linux-ppc64-gnu@npm:0.44.0": "b0a0d63384eeb33fed63d353b292db5ef1dac113b692455b36f462d97f66b8d719a07b71affe63f6f555856d1261c24eccccadc72f04f720b7470321aaacc67e",
"@oxfmt/binding-linux-riscv64-gnu@npm:0.44.0": "b005497fedfde005c8165a646d4b7faf35c63242458166c130d7e15e6eb8c285cfc9907cf44885c05f4976d99366064ce13eadf2d6c7867b75f9ad28765720f0",
"@oxfmt/binding-linux-riscv64-musl@npm:0.44.0": "2f95d5cfdbba63b9e74158ce9c64262f1be28a2483c961f7e376d27c43fc582ded2a2358d459b1f503100c766b5207e7e77fa2e27e69f2a3975ddb75bc9d93b2",
"@oxfmt/binding-linux-s390x-gnu@npm:0.44.0": "bfd75043bd870f1b5142966383d7256f3fc596e6ce2de4365e964efc0c6f716f222d9dfccdaa901e2b55edfe24a6ef83a2ea70ad2b79c31da1c9507d157504ec",
"@oxfmt/binding-linux-x64-gnu@npm:0.44.0": "4039e8c3027499baafd8da1e612c6fe01d0ce86c7993064c133e879c64355f02f9d9f951388919610264fd33c2917fb6535bb0c72e3b222cd3bdc4bf4beb8471",
"@oxfmt/binding-linux-x64-musl@npm:0.44.0": "7dd406f8d19af090cc6c42aa9d420a5d074c15a0a214cfadb9c3c6a6f7588eddb38a91595371b0d9d247635ec7d130e349e93edec7be759888b702b4a8405919",
"@oxfmt/binding-openharmony-arm64@npm:0.44.0": "5b9078488871e85d57a5af8eae390dba125f37a5ec1e1d120ee34d1d9b368f6b5715f26d3fcb3adbbc1d2f06cc928e3f73103f116f0323fa02d0f5305621e7d3",
"@oxfmt/binding-win32-arm64-msvc@npm:0.44.0": "d9aa3808de1a6e910e463235674cad17f90f015883bd1a74bdf9a1eb17b9ff0733f39f4d0a3af9b005fd55ec5584393fd1a6effb2858bd677aa205a2b8ed153c",
"@oxfmt/binding-win32-ia32-msvc@npm:0.44.0": "b441cb9974a64d1e6dbca885000aaa76e36e6bf99020bd7172238b60c165587de737c6715259b19ac982bb5f3d008a1cf7088f8be06e4343c4acac7702da88f3",
"@oxfmt/binding-win32-x64-msvc@npm:0.44.0": "35e4722f8594092dde9b5ee51693766928205c7bbab44e25710584ac13ae71460bf3dfe34d7222eba5d24d8bfb58700cf551873bea876858c1cb3fdb355f9945",
"@oxlint-tsgolint/darwin-arm64@npm:0.20.0": "34285104efbf3569d3c9dcbb4ce65c33970377fa2f5133bdc58a0b8dcedca269be03dac054c031f594782487b97a59b93084ea525e4acba5c4e45d10299f813a",
"@oxlint-tsgolint/darwin-x64@npm:0.20.0": "d5f5f6aeeb1bc08f1c74a7952450da97eeb4a8970bcb28645121cff9c9ce3cdce03da60c4814769f355ec9cc1c8e2b70f0ed268117cc383569c7c3b4da617d24",
"@oxlint-tsgolint/linux-arm64@npm:0.20.0": "93d1bad95440a976e8c241c1e233151bbe4bc8780bc65ea3605fb19851c59b8f7cc45fa0797398620c31b6b93c261964696bc9ab8fddf6ae625d2ba4981f171d",
"@oxlint-tsgolint/linux-x64@npm:0.20.0": "09a6529dbc46cd4b1f4f650beff905d306c264638a27ad36c46ef8631f64f689d660acdb1ced87c72406fd380b9aaa95599e6b99c73438eabc1854fd920cf6ea",
"@oxlint-tsgolint/win32-arm64@npm:0.20.0": "422cc8a91a65cb4bf39306da2809e963cb15fccd7600b7d99a6ada0f10cb6934ae1b6f25c881c27a2651e0bd601417f7cf6dbe65dd32f9bd862b5a76d8227a99",
"@oxlint-tsgolint/win32-x64@npm:0.20.0": "547630cebd56abeacae7af89df6fa46b9165732b34b72e6a0c95f2d619db9307bb0d4a7bae8039426a34c3412681962f6ee8cdd1db0bd333e287da6322b91766",
"@oxlint/binding-android-arm-eabi@npm:1.59.0": "fd7147e2bc930ffe34b5de0de3195e20372ce9a392bd0782f9d96ebe19e63cf7969da89cf7060d7f43ce1bc429ee14bd5b8dc8874fda3a5d0bc782d1cdd0662f",
"@oxlint/binding-android-arm64@npm:1.59.0": "f674440a9751e274d0ab588539cc13e5abe0c6464abdd423968572b034ff3e54c6f2a173d22df0425e16c997caf69b84b955f8457cacc908f331f54df6de2821",
"@oxlint/binding-darwin-arm64@npm:1.59.0": "29484896b54454e73932476adb9740c5bbac166c9fe138a6df921659efdca0b679822e463dae8b0baad54a30b0deb9038b0677ddcbc11680defac9df42b644cb",
"@oxlint/binding-darwin-x64@npm:1.59.0": "b16f0e20b766b66e71088ef484051ea266c9f730358aa1e41e20f02ca2a1455eed8133c3f7d79486ad0aab1930915bfe75426c2c16b8204d3e10f9a489cd0d5f",
"@oxlint/binding-freebsd-x64@npm:1.59.0": "5c00690cbe3fcdf6634e2e5424d1a415fc455cbd1bae264b3a0ce3ee3eed0eb8a0b825a2fd016c04afe79c863574557eed74734c5fecbd8643d62aa1d74219fc",
"@oxlint/binding-linux-arm-gnueabihf@npm:1.59.0": "a55e2a9ac39526a5a432fc4bc69f056e5507ad7344b082dde448e70fbeb7c180b1edcb62c377d38a99710662d8b935ecf2bdeb75ebf2b9e9425e6179616fc7a1",
"@oxlint/binding-linux-arm-musleabihf@npm:1.59.0": "5068f1da0497ec448e87cdc1c5ee99bfde09a050ba989b8dd3df445199360bde081eae327f52f4b37572ce300f3295444ba25a39108c19c93fa5859b9d1c8e5c",
"@oxlint/binding-linux-arm64-gnu@npm:1.59.0": "5a3a974a344731db64951c3aa3d591f82a2c69926bfaec1d42338aaa0a65cea97c94a68121d22441b655d80bfc0e8dfa81944136ed5a0abfb1353a46105d8478",
"@oxlint/binding-linux-arm64-musl@npm:1.59.0": "a2158b760c37796f5f416a2be77110935329a55788752073ac257de8d874a57cf954b90740dd1952c2f63c6d3958dad0077681de098406d4c3586ca92061804a",
"@oxlint/binding-linux-ppc64-gnu@npm:1.59.0": "88feb1b571f3c0eed7f020c5f06d729087571e9515a309eb3a54b25630ef0f951c07f81495598d634b0edc75abad07f1c1e595e2f42b5849fc82c90acf98825c",
"@oxlint/binding-linux-riscv64-gnu@npm:1.59.0": "88805ce56f0aef2dd0bc943bda3f1d2a4ca4e68765478714c26dbe95c42a7c3c8087b2658501ea36d1303b8f2aabb9c0d68586124cc0a20f8fca70bddfd598c4",
"@oxlint/binding-linux-riscv64-musl@npm:1.59.0": "1d99bb0491a1b02f2b2a71707d73527b1e43d18d0a5b1d964d6752af748f1e6ea73eb60d66dbce1681feba48152c636639949e9d3ab558e2c76ba58372ce280e",
"@oxlint/binding-linux-s390x-gnu@npm:1.59.0": "d5c3a9da18eb31597c59c17bb03eed3ca77360c0a8609097bf367f1fdcd33cf0c1a604cdec46376ff63c7741c723df060fc5b78adb256913f567a424996f665b",
"@oxlint/binding-linux-x64-gnu@npm:1.59.0": "963388bfd60e5497ec0f3e6d05b4567ed795f132f47cc7c112acc18231225403361e6d4f8b35923bbead94d9864e6c76754014dd6ef779dd85d4f9070605dd14",
"@oxlint/binding-linux-x64-musl@npm:1.59.0": "782a7fa5b28a87c2fb96fdce112625906c78df953b141f0d43e7c064eadbe4694535cddcc2bd05c7312eed0147002f4c45faabb1990e282cc0471de8e841628a",
"@oxlint/binding-openharmony-arm64@npm:1.59.0": "2a6bfe67c9b1ea1cd202bb84a7dd938343735afedcd2ef975f11879a0cb24a1fb7969ef47c9aa1d3f4255134483254ea83dfed7c1c674eaf9e0412371fa69b1e",
"@oxlint/binding-win32-arm64-msvc@npm:1.59.0": "2d8a5052bc2d959ddc57ce3ccaba237e4976e91a8bfaa0b3a658c05ba7b771f44098b406bfc610e0a2b25252d0b11f6d6240af9f095850e49d5252cd628914ba",
"@oxlint/binding-win32-ia32-msvc@npm:1.59.0": "1f17010c128674db9782107427e6db331aa53f437bc9fe2e97a9d37d8b25f9a011b709981af0ec1e0af7375840ffa4e3dbe9e10494c2aac949b71206cc934a63",
"@oxlint/binding-win32-x64-msvc@npm:1.59.0": "7d0562b3977c612e1278f8a2ed06e7e3f19a1242ba50510a68ca6adec5e6fa0d5e2aca50dbdbce69088991b233598aa8c514c5d84f5a5c1fa039b29bb453d49a",
"@parcel/watcher-android-arm64@npm:2.5.1": "e9c94ede3bd5c5d999d117d22ac8032a17f8ebc72db3eff04ccb2b4e6718db19f24bf29a66a610e03f4ee95e2cd7b2d30c15b1845eb897b971fec75dbdd76141",
"@parcel/watcher-darwin-arm64@npm:2.5.1": "0cab55a55c128ac5742388fc8dbfeb9877018509943801ce8a52b57bb6dca24189d025d38684b1e482cb7816368a52c6434dfe45d3997e2fd2509276f48774ea",
"@parcel/watcher-darwin-x64@npm:2.5.1": "bf07b8ca9a435fb885fb0ca6565204d2f2098d7f632faf26a6478bb39f538c73b50afca17c193dc189a80a864d85e40f924ec7f21a0e7ad7d0de6f97f7154134",
@@ -122,21 +122,21 @@
"@parcel/watcher-win32-arm64@npm:2.5.1": "0f467a731cf9403b8bc7d35418d991596cf5e7898029796b4c769bcbb38cd07ae6ec05ef0f19298e5f11e73ec5198bc474d79b056bdfbaea513525725103d7dd",
"@parcel/watcher-win32-ia32@npm:2.5.1": "9ab5f3e9849a6077c8c2aba7bdf9030dea38f0ab9180792ecd30094520cddf16f3b68006f666845b86c5ef0e05c648364475c9ba151e0269561891ca3e276667",
"@parcel/watcher-win32-x64@npm:2.5.1": "e588d87d5b892484d252ac8e1ec3f4bf7a664d91f0d03dd93764be8db2c35f81879275908dcbec42b0e43bc99c7afdfd29fe687ec022bb2c8c4bc7edd29eaa15",
"@rolldown/binding-android-arm64@npm:1.0.0-rc.9": "681d0f60ac8346ed1472b446dbb04c89f082d05eaadd643093f861d9782b8b9c961af1529ac50ede6e1d0d82162413be7996cda3582c6839fe8ce110e6a8fe47",
"@rolldown/binding-darwin-arm64@npm:1.0.0-rc.9": "1aa953031ac0743e3c21c30d5423f91910228d3ca626178dfb6c526d7101ce0f782738c4508fd5c8c8b13db1a7cae7c60f56645903d1eeb6acfcd62d6038a62a",
"@rolldown/binding-darwin-x64@npm:1.0.0-rc.9": "04b667387c89dd09c16f19d89779faab109ef77747d574c9d19f03a45c6199d761c50f252ebcd72bb2f1a963d5159562e6b9c812412b59191cb5ad39976faf85",
"@rolldown/binding-freebsd-x64@npm:1.0.0-rc.9": "be6560cb62878c11073455c3f8c9d60f27d42e847e6280341bb3f203344b45f6ad0045217229488c13f02a41b5b3f9c92cc4e3e5eda3167e92975ec6690559de",
"@rolldown/binding-linux-arm-gnueabihf@npm:1.0.0-rc.9": "d943e78b207ab210e0e30080f3ee67294e22346d44535e1e06252b954cb0df5188cc0808412b3c74a90b07f4b380f0ba00ab3c0500f8f59ac97c5a79d52a256a",
"@rolldown/binding-linux-arm64-gnu@npm:1.0.0-rc.9": "db9fbe0bc638788fc2bad53d63b9943af0472f01f37eecd552d06bb773f3ce6594fe7cf50b2bd8f2d5b72bc60b9bdcfc5b8e45ea8ea5d368bdc062d3104f37f2",
"@rolldown/binding-linux-arm64-musl@npm:1.0.0-rc.9": "1284b2b09a688dfa979d0b3ba1969ae30ebd29fa56f700d267f1862aa14327fd58bb92e8e201f166c31e916d9c2b3e56a991dd5a83efeedde6c6b3925f066aab",
"@rolldown/binding-linux-ppc64-gnu@npm:1.0.0-rc.9": "4c051717e829c1a412c924e8c02f2b665fece40d00945d73c533dae75d68f3a0c7a219abab602234a6bf15049c9a1861e3788a97e9f322aac95e91c6d12a2db6",
"@rolldown/binding-linux-s390x-gnu@npm:1.0.0-rc.9": "63528c47def8780bd56f0851d9d63fa9a4aff24e87766989cd3b30478f3e0240a38ada7df67343cc8c075f2f907b0ba3feff1de1fa312727446fd762b313b3ff",
"@rolldown/binding-linux-x64-gnu@npm:1.0.0-rc.9": "9eee8d3facd1d0249f0e8c96a0cd0021875a6df39b00fee0a88629a1d3f83210e4c8216b35ce31b3cbe8e3399829fb10b805bf6ca6d2116658c8bbe5a63b93de",
"@rolldown/binding-linux-x64-musl@npm:1.0.0-rc.9": "542bc8bebc8c0373a3d6a7d9a8a3241cddf1cfb64bea0a93e208410d88ea4b6b40e3ade7615728203460c98055269386931faf79e3b94f184446241f2873a55b",
"@rolldown/binding-openharmony-arm64@npm:1.0.0-rc.9": "4c3f8c7e29c7f2e23c8a98f16bc88584b6c770e03ba4278d7730d844b9595cc9fe486dd8db8b5df514351c202de41dfeec29e9cb49a345b68fb89e751046355a",
"@rolldown/binding-wasm32-wasi@npm:1.0.0-rc.9": "a4f8b94a3c8fc67890e4ca4115a58fa096cd17060ff6a04f296fdf4e9925f88dae82e6ac9213b63b3bd1e44dd5454f7f37d24c5710e93466bc565f2eada56508",
"@rolldown/binding-win32-arm64-msvc@npm:1.0.0-rc.9": "ea4b5f173b212c0facc574814c7f303cf7ff3819e190ad2cbe0cc945853e01f984e3db475d586276c9d57bb33eaec3cbbfffda202dfba22a969457808b8f9037",
"@rolldown/binding-win32-x64-msvc@npm:1.0.0-rc.9": "33ee0f9797ff858c5af077b5907a7becbd2719c76afaa8069cda7fb911d404fb4b038825f8c976efc3ee0bd6dc882a9d4c4b240ab9cf8000ddd720d551ef5a8a",
"@rolldown/binding-android-arm64@npm:1.0.0-rc.15": "d93872015985b19d82db7a99deddfd1bc65c3eed7928004c36cec11b70e405b82907250c183367b47ebfa2bec83582b3369b91d288ad5308152ceff860ec2a8f",
"@rolldown/binding-darwin-arm64@npm:1.0.0-rc.15": "1b8240d7b1f0601d6efa84d48a232e8b0e6239477634c1b7af5b27055a10aacb893522ceb08fea8800e7ce5ec882307e069077ec8626039684a323a815ea1639",
"@rolldown/binding-darwin-x64@npm:1.0.0-rc.15": "a73ed937b8e7dde059cd600612c359ba02dd4ef3ebcd0df3e5a2e5c0e1a7e4c8360fbfd40c066de9a6f2af471b8322b49471a6261bd322417e9c7f955eb52335",
"@rolldown/binding-freebsd-x64@npm:1.0.0-rc.15": "f7c48373838b49a1f3ca592299c9e21b829e4efae62e9980a097655c01c9ca65f079e0d04bb5604887b09660d71ed6c83115dfeb94cd7d7cbcd67000a4e84edf",
"@rolldown/binding-linux-arm-gnueabihf@npm:1.0.0-rc.15": "0681132aee29f8ffca4f436034f4bd6ebc4ac9ef15ac9857be09f991e9a1367f3e97d98f88e136659b5a5cab520f5a304e0551cb0f08320426f718c6475e64db",
"@rolldown/binding-linux-arm64-gnu@npm:1.0.0-rc.15": "9f1be6a7f61d913d678d77b21c499dce756d5b4d496c7665eb5015a7f9b0e52038121ab313b223448502a1f23f39f4952ccaf4651d65821ef8d0428730dc5d43",
"@rolldown/binding-linux-arm64-musl@npm:1.0.0-rc.15": "c13142d79451dfe6c38cc18f1ec170b87ef95546f45a465b9965b6f8ae8b162d278c77654400e69c9af7c09c8eb84c59edce6f741bc08e2f3553b019c40f51ae",
"@rolldown/binding-linux-ppc64-gnu@npm:1.0.0-rc.15": "10a94ad784a1997f5fb3756e44282b33524b32cfc52c622e81bcc1c178fcda3ca44734bd2562dae900e0637233bccc94f298764fd43acd5037c4b66aafab7651",
"@rolldown/binding-linux-s390x-gnu@npm:1.0.0-rc.15": "ef16b67d35aea455ae16c07dee6e58ec5515e2dee2b9ce0fa38752e15d12b2398f3e032485041e6234028d8ed85028c1072d73d2b0a912e0de5082ea89144cc6",
"@rolldown/binding-linux-x64-gnu@npm:1.0.0-rc.15": "8eb51515e2a1bd05120ff74728e972aa563b0c6d6968f78a79a9949b008c9826875c4fb74aadc7c2a1bff065d91970b706290a973128066c076007f471940f31",
"@rolldown/binding-linux-x64-musl@npm:1.0.0-rc.15": "1169f12fb17b8a58d5085a9968e4bd92b63bd05a9a693b9113b15b01d53c482150bdfa2226790d59c398f49e58d93200aa7a297a7994a31cd343152303a000de",
"@rolldown/binding-openharmony-arm64@npm:1.0.0-rc.15": "c34353635835a2fae1e637f7a02266204767901211438bd85c8d887bf741b81367ed3b5eed595f3cd707fe3b8af14a7ff53ad211f2a944e6e9e74dd4ca09d73c",
"@rolldown/binding-wasm32-wasi@npm:1.0.0-rc.15": "429df3739d6257244ab5d481fcc4b575c6c3f50bba7afdd9f3515832e7ae5175a215dbf591eade537fb2e6f7af38bc8022bc55bfffabfe650048563d65151304",
"@rolldown/binding-win32-arm64-msvc@npm:1.0.0-rc.15": "3cf2f7babe9e9b02b0dfa58b3eb8204753b4ed1eaa1e8567a932d21a5d90003dbb4b6279c928f15dfd9247dcbc8aca36fa6cae7790f25cffb91c3d4eacb7c0e1",
"@rolldown/binding-win32-x64-msvc@npm:1.0.0-rc.15": "cb262337da8a42a84b53430c101407a72c142b947a4588e55d6d1ff667e853fc704269436703761b127bf4895c1e205ab33b4630e603b8b5bef35aebe2c36afe",
"@rollup/rollup-android-arm-eabi@npm:4.40.1": "24131de0516010cd1c52d399fb17dbbe93b3ed6a981b57d82f363b9102a04083d470d0d7ead324fcc16e0eee7d54b7300ed3ce72bf43a4d277b959eb79f7490c",
"@rollup/rollup-android-arm64@npm:4.40.1": "6b02d5c1a8e5cb045ea9d382d71a9bfcd81da1d508ebeddf5c5118f99ce13b431af357a0458d922c03b26a00ca6ea72a861b1e15b370f20275ee73eff91fd7ce",
"@rollup/rollup-darwin-arm64@npm:4.40.1": "2c47b17a3670ff37a3a4ba6615f1514284317adedbf629b9d989f879f2ccd0c299f69c7733727150206b1edd96605ab9d94b6f0eae70cd7556542b436111d5c6",
@@ -157,23 +157,13 @@
"@rollup/rollup-win32-arm64-msvc@npm:4.40.1": "d4c9002b95c4b6ee842faaa5087582a0772578c0969c9ee470697a6d3fd251845481285f5a4027bc8c0524bf277f3437844cae9936c5f96ca753dcca61e2f47f",
"@rollup/rollup-win32-ia32-msvc@npm:4.40.1": "446e3ce5b037d1847af84e9a2b52734ae9f5425937fec2558d26ffb5c253dd8925dc35591abd78b0d43f7154222e47ee9aaeb3b167b3d69627805a97c5147185",
"@rollup/rollup-win32-x64-msvc@npm:4.40.1": "39fe3c6b86ef880fb1d1261f6096d19707584c628271d6ae01f5a5f50d8a24ef62128b5929064c0aed4390c7c0c0b7cc9590e300fa5f10ed624816748da2fbb2",
"@swc/core-darwin-arm64@npm:1.15.18": "1597877545538954d6445a21f4a102766f098aa2b0d55ded3a766993b527bf1bae9bca54e50e3289b8f6a0cdb42b550f65da38564340d954690df15d2e27f56a",
"@swc/core-darwin-x64@npm:1.15.18": "326b8c3309c303a53bf4ed6afbfb0baee0acdb527253e734fe5cefc6916cea598d75fb508265e482143e92fabaf2cacfe9e3a30301ed527c32a727f998a65239",
"@swc/core-linux-arm-gnueabihf@npm:1.15.18": "0d26738b4675ee1ff5b0322295c947f351e6d2494303248971b601e2fcfd666a17357a030f0a745e11b60065329652daa58db44abcfc7620f2da8d92733dd0ee",
"@swc/core-linux-arm64-gnu@npm:1.15.18": "96aa824334204b6ae8a9c337a745a64dedd4913331f313561eb9544b853e142d9a933230d06052eb677cffbed0b1c961389f1d626936a9b5fad6400ad254229e",
"@swc/core-linux-arm64-musl@npm:1.15.18": "3781cb4bfce767fb85506a69bc6fb7745ec53be2a53735750ae88f51c3d6803725a3a906cb954888c529e7f675d3ff0f746bb22c646f1ac2caebdc2be64a852e",
"@swc/core-linux-x64-gnu@npm:1.15.18": "26f6fe774e88f8d58a48c18666eda6345cd7064cf065f02eae683f6eaac3e345a5fdbef22a8b3aa7069e332fe95c4b496cbab438bc2b474a9fdc8b37a616be0a",
"@swc/core-linux-x64-musl@npm:1.15.18": "96b5bd1bbacebbf806f9a6a02698d68ba2a677ce86b5c2d7a92ea6083b40d55a0a84abebd16b5afd7a574e727b8c19e9039315e8ef40f6c852bbfaee26f999cd",
"@swc/core-win32-arm64-msvc@npm:1.15.18": "3a170183da529015301e48bd305f9ea1bcbd38259ff25f1f6e778ee4bb68fda44552c696f067005729aa77407295b1388717a31d9429844725d7ee29645bde82",
"@swc/core-win32-ia32-msvc@npm:1.15.18": "a950c9d1d6338939734249ddb766c495e86e05509bb6a42f58d2713d419d4412c06fb0c11c7711c6612ab3e74520a8c1581449340aa3f4fe26f71c53675f37b4",
"@swc/core-win32-x64-msvc@npm:1.15.18": "68eac0d5148e7326b748a0f0adf44bffcfb1d0c78e366448aad9859eda537941391ea95e1863118460a49a58235ce9791ae1b20afc04a034677167e6ea43a5e2",
"@typescript/native-preview-darwin-arm64@npm:7.0.0-dev.20260309.1": "437a0ffe06d99181f7dc9af64bd049ec1ec84f2a4587d2cfdc8fb64cffac527de6cb710b100f87a7ae4b97fe3cdd876466f570424399a1b71496c526c7cbec13",
"@typescript/native-preview-darwin-x64@npm:7.0.0-dev.20260309.1": "02f218b9974f2ff8afcdc1716ee04d4021084c278d0e51a5cc5db2b8ffc590cf96877c95178d86157e0440513d7427e562c5d63b9e00ea81396dcb796385e789",
"@typescript/native-preview-linux-arm64@npm:7.0.0-dev.20260309.1": "c3a070ae31cd0c2e2750c5ba2332352bc575a0be06e6923b8aa0f425d50c95b922df9ae540c17f87affb61948555db63ea9f21df6cb70b2345632f1bcedb2d87",
"@typescript/native-preview-linux-arm@npm:7.0.0-dev.20260309.1": "08c8f9a22cf9f1508c84cd8710a44e4983d41df95a58e79a9b32c177e1044f86d0b19b993cc0bfcbdba0b9ff12df1f22b878cd33cd20288eedd6903b25b69608",
"@typescript/native-preview-linux-x64@npm:7.0.0-dev.20260309.1": "7baa4d2aa516e2af41a255c2596c795f8a782d4f053ae109e08741eb67a6d5ebea630e29656519f4369b3a71d23467a50914b02206f4118d5135d0a71398724f",
"@typescript/native-preview-win32-arm64@npm:7.0.0-dev.20260309.1": "0b6ff10d8ed63f11655d8750c723711c22e8f8eaf0a9a5958f6059dbbaf485fbbb2e673a62bf913178b3ea3d101a60ca33a667e059d1739714feb46e41f8079b",
"@typescript/native-preview-win32-x64@npm:7.0.0-dev.20260309.1": "d55b53ab9065c600e50f1f029692d1fed4cc62ff9672ef758c70754168c310853aebd7f231cdbf8eaaf8af85015b42a9a6985dbe387bc88e648adab511a00873",
"@typescript/native-preview-darwin-arm64@npm:7.0.0-dev.20260421.2": "12273bab953bbacf429e4895db7ac5e40b05f3e1511b7053119eafad99af9241f893e975e29735403f7247a4a1a22f56a5eca0d84ec300dc705c77ebc4292b61",
"@typescript/native-preview-darwin-x64@npm:7.0.0-dev.20260421.2": "711451bef67dbe18e0a7fdfd5915f74935d76c8d22ac1f0bc74df55af66d6a782f7b91f3cdc7ff4773f02c5c84fbd3c79061e3888707735089d45148dc262910",
"@typescript/native-preview-linux-arm64@npm:7.0.0-dev.20260421.2": "b1ffbf30be075be6cf8ed9da314326ca4050a31ed33fde3daa90fddc37518d62695abbbc1b1f212cbf160c22fff760fa2ee9b447a16fe6e14fcf36329df9bad5",
"@typescript/native-preview-linux-arm@npm:7.0.0-dev.20260421.2": "c7e6be5c67f8010519e3240eb821b452beed3dfffe880a5cd8ca19f1873e1c27283af100d9e2c0175b624dd4042337e1898b1c3e3c1122605f1e9d7c56638b24",
"@typescript/native-preview-linux-x64@npm:7.0.0-dev.20260421.2": "73a130ffe78755e48254d12d82399a5c479998142e98273d4edda8f10806ac31a4e52ea4ee61dd52999930545b93471e8620746f8552ea275b2a2f32b1cf4c8e",
"@typescript/native-preview-win32-arm64@npm:7.0.0-dev.20260421.2": "903319a110c6348185aaa508db50d48ce0c981c5a745cbc57c36271caf6f3b37b7a577e549594edd1b185cd45f7bb50c7c98ac1bbea7f800bfef5dbd6d3d42bf",
"@typescript/native-preview-win32-x64@npm:7.0.0-dev.20260421.2": "a46bf36e4df25ac9bf60a39b4a4953d4cc43ec40c55bd1de9bb088ee4622d53955b39c07c8ed16b21c4bf841f22f997e17d9af50a376fd3c0349cf36e3857dfc",
"dmg-license@npm:1.0.11": "36c0a7b030801b91216affa9b2bb00caa345b2327f298accb2263a80a0320ca305f90b99da68007d187c830c543410d58a0a2bbc229e8d169b0e1d1652ff42aa",
"glob-hasher-darwin-arm64@npm:1.4.2": "1abc74c6f6c6251b8bea6e412090eff4e4918f3489a371971840ee81534344b9f9e62a610efb98157970fc46561161bf23382c8572578da98a7e884d6fb6853e",
"glob-hasher-darwin-x64@npm:1.4.2": "44110045f0b2e0b3abaf4f70f917a3c57b9b0c6ee56e5d02932e1772cc63a36b066fb3bc1e6a275c40978b3b2d2ad62752e899bcd966930d4df8884b1d554764",

View File

@@ -14,13 +14,13 @@
let
nodejs = nodejs_22;
yarn-berry = yarn-berry_4.override { inherit nodejs; };
version = "26.4.0";
version = "26.5.2";
src = fetchFromGitHub {
name = "actualbudget-actual-source";
owner = "actualbudget";
repo = "actual";
tag = "v${version}";
hash = "sha256-Gc2klYxGv+vd1Yc2ftj25B4Kea0GKkpjYcVDN9HvLPk=";
hash = "sha256-bcQAlG9acxTSqOQiSr1pmk4A6yjDWD/QH3AeYtqgAdo=";
};
translations = fetchFromGitHub {
name = "actualbudget-translations-source";
@@ -28,8 +28,8 @@ let
repo = "translations";
# Note to updaters: this repo is not tagged, so just update this to the Git
# tip at the time the update is performed.
rev = "14c3f5e7ed4e47dedab8cebeaf5e2170cfa5f9d0";
hash = "sha256-+4hENE9unsta1YoIDE7shcjy1AlWfnPczvm4jYnw5Dw=";
rev = "1713f1230b8643c39aece866de755976707a4060";
hash = "sha256-79WpnFsGkpkWA7qm19YQ41TYu5qxXCcecYHNvx3KGQ4=";
};
in
@@ -105,7 +105,7 @@ stdenv.mkDerivation (finalAttrs: {
missingHashes = ./missing-hashes.json;
offlineCache = yarn-berry.fetchYarnBerryDeps {
inherit (finalAttrs) src missingHashes patches;
hash = "sha256-7Vlc9hPv7Sr2ZUw7fasl3xf7ZYU31oS4tWW46UBJ1F0=";
hash = "sha256-yHvnahriFO4Yuuf+NrfHWQhH35T2eHmVOGw8SqP856Y=";
};
pname = "actual-server";
@@ -130,6 +130,7 @@ stdenv.mkDerivation (finalAttrs: {
yarn workspaces focus @actual-app/sync-server --production
rm -r node_modules/.bin
cp -r ./node_modules $out/lib/actual/
cp -r ./packages/crdt $out/lib/actual/packages/crdt
makeBinaryWrapper ${lib.getExe nodejs} "$out/bin/actual-server" \
--add-flags "$out/lib/actual/packages/sync-server/bin/actual-server.js" \

View File

@@ -2,10 +2,10 @@ diff --git a/.yarnrc.yml b/.yarnrc.yml
--- a/.yarnrc.yml
+++ b/.yarnrc.yml
@@ -6,4 +6,7 @@ enableTransparentWorkspaces: false
nodeLinker: node-modules
-yarnPath: .yarn/releases/yarn-4.10.3.cjs
-yarnPath: .yarn/releases/yarn-4.13.0.cjs
+approvedGitRepositories:
+ - "**"
+
@@ -15,9 +15,9 @@ diff --git a/yarn.lock b/yarn.lock
+++ b/yarn.lock
@@ -2,6 +2,6 @@
# Manual changes might be lost - proceed with caution!
__metadata:
- version: 8
+ version: 9
cacheKey: 10

View File

@@ -26,13 +26,13 @@ python.pkgs.buildPythonApplication rec {
patches = [
# Backport authlib 1.7 compatibility.
(fetchpatch2 {
url = "https://gitlab.com/yaal/canaille/-/commit/b356baa82109a7fdf61a8258572d199ffd3c9604.patch";
hash = "sha256-f5zJBtm9mJpWra5x4vr07eL9xe381lLFp3lfehc4Ypk=";
url = "https://gitlab.com/yaal/canaille/-/commit/b356baa82109a7fdf61a8258572d199ffd3c9604.diff";
hash = "sha256-/U6S3h6qIl763ZsGpOm6CVk4NaY3A7mq3PkT193aLEs=";
})
# Update OIDC tests for authlib 1.7 behavior.
(fetchpatch2 {
url = "https://gitlab.com/yaal/canaille/-/commit/c1b6d103ebf374cd6a21d9af8376c910c2d0d5d9.patch";
hash = "sha256-N9kxiKU6iwXq6cw6itZQHairSOgyDgwAVByj0NnxMqY=";
url = "https://gitlab.com/yaal/canaille/-/commit/c1b6d103ebf374cd6a21d9af8376c910c2d0d5d9.diff";
hash = "sha256-MjwkUb54ikt1+xUXBTOIBi9E+DmPdwYhw0W0c0prF/Q=";
includes = [ "tests/oidc/*" ];
})
];

View File

@@ -21,7 +21,7 @@
buildGoModule (finalAttrs: {
pname = "grafana";
version = "13.0.1";
version = "13.0.1+security-01";
subPackages = [
"pkg/cmd/grafana"
@@ -33,7 +33,7 @@ buildGoModule (finalAttrs: {
owner = "grafana";
repo = "grafana";
rev = "v${finalAttrs.version}";
hash = "sha256-fGRvCDIOQcF743SimatyNmX0gZtO0tvgEAFuk38rl88=";
hash = "sha256-v/6a0Xlj11nZ0u8xtFzw5i674rB1YV21iLCuLc5af+I=";
};
patches = [
@@ -55,7 +55,7 @@ buildGoModule (finalAttrs: {
# Since this is not a dependency attribute the buildPackages has to be specified.
offlineCache = buildPackages.yarn-berry_4-fetcher.fetchYarnBerryDeps {
inherit (finalAttrs) src missingHashes patches;
hash = "sha256-l+X7vgU+wuuq+Usdp0ffY1SpT70QlmdsjrHnyyJufjw=";
hash = "sha256-uOl9PemVZiKwGfhLUpAAByMvt5A8JyA5qyJ6Cdl6od4=";
};
disallowedRequisites = [ finalAttrs.offlineCache ];

View File

@@ -13,20 +13,20 @@
stdenv.mkDerivation (finalAttrs: {
pname = "lasuite-docs-collaboration-server";
version = "4.8.6";
version = "5.0.0";
src = fetchFromGitHub {
owner = "suitenumerique";
repo = "docs";
tag = "v${finalAttrs.version}";
hash = "sha256-8xMHHyj9qUdrd5dFYVlN2bi7EVjcEqoBBxIifC8xk3k=";
hash = "sha256-yjcnXC46C2Z453oN4/fJc2q+B0yQKL3jKaIIpRlzu5s=";
};
sourceRoot = "${finalAttrs.src.name}/src/frontend";
offlineCache = fetchYarnDeps {
yarnLock = "${finalAttrs.src}/src/frontend/yarn.lock";
hash = "sha256-4jaKWepa3+SxEVS+gF5QrOeJaOpS8vzFXZyN9SxClUE=";
hash = "sha256-K7AvCt2GMwo+mtTqa3c0OGUGM3Whfo/WfeYG/Vjxhtg=";
};
nativeBuildInputs = [

View File

@@ -12,20 +12,20 @@
stdenv.mkDerivation (finalAttrs: {
pname = "lasuite-docs-frontend";
version = "4.8.6";
version = "5.0.0";
src = fetchFromGitHub {
owner = "suitenumerique";
repo = "docs";
tag = "v${finalAttrs.version}";
hash = "sha256-8xMHHyj9qUdrd5dFYVlN2bi7EVjcEqoBBxIifC8xk3k=";
hash = "sha256-yjcnXC46C2Z453oN4/fJc2q+B0yQKL3jKaIIpRlzu5s=";
};
sourceRoot = "${finalAttrs.src.name}/src/frontend";
offlineCache = fetchYarnDeps {
yarnLock = "${finalAttrs.src}/src/frontend/yarn.lock";
hash = "sha256-4jaKWepa3+SxEVS+gF5QrOeJaOpS8vzFXZyN9SxClUE=";
hash = "sha256-K7AvCt2GMwo+mtTqa3c0OGUGM3Whfo/WfeYG/Vjxhtg=";
};
nativeBuildInputs = [

View File

@@ -11,12 +11,12 @@
yarnConfigHook,
}:
let
version = "4.8.6";
version = "5.0.0";
src = fetchFromGitHub {
owner = "suitenumerique";
repo = "docs";
tag = "v${version}";
hash = "sha256-8xMHHyj9qUdrd5dFYVlN2bi7EVjcEqoBBxIifC8xk3k=";
hash = "sha256-yjcnXC46C2Z453oN4/fJc2q+B0yQKL3jKaIIpRlzu5s=";
};
mail-templates = stdenv.mkDerivation {
@@ -29,7 +29,7 @@ let
offlineCache = fetchYarnDeps {
yarnLock = "${src}/src/mail/yarn.lock";
hash = "sha256-B2vtdQYFhhsA7dK5nwAJl65kaedspfYySJJBjVwYeBM=";
hash = "sha256-g5MYtHvs0i0AOAydMxJNx1xTwbZtXS0CYDNQC+cnIOM=";
};
nativeBuildInputs = [
@@ -57,9 +57,17 @@ python3Packages.buildPythonApplication (finalAttrs: {
./postgresql_fix.patch
];
# They use a old version of mistralai which exported a class
# at the top level
postPatch = ''
substituteInPlace core/services/ai_services/legacy.py \
--replace-fail \
"from mistralai import Mistral" \
"from mistralai.client import Mistral"
''
# Otherwise fails with:
# socket.gaierror: [Errno 8] nodename nor servname provided, or not known
postPatch = lib.optionalString stdenv.hostPlatform.isDarwin ''
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
substituteInPlace impress/settings.py \
--replace-fail \
"gethostname()" \
@@ -75,6 +83,7 @@ python3Packages.buildPythonApplication (finalAttrs: {
beautifulsoup4
boto3
celery
emoji
django
django-configurations
django-cors-headers
@@ -100,6 +109,7 @@ python3Packages.buildPythonApplication (finalAttrs: {
langfuse
lxml
markdown
mistralai
mozilla-django-oidc
nested-multipart-parser
openai

View File

@@ -16,14 +16,14 @@
}:
stdenv.mkDerivation (finalAttrs: {
version = "4.3.0";
version = "4.4.0";
pname = "libdigidocpp";
src = fetchFromGitHub {
owner = "open-eid";
repo = "libdigidocpp";
tag = "v${finalAttrs.version}";
hash = "sha256-f5wU3C6NC4op+9Wy+khwNJ6slFyPhq7hZl1Tj5hnYc8=";
hash = "sha256-Rf4ex9UT+Bspkf+WNNpYpdIt7y+QjAZ+eg786FZ0ZsA=";
};
nativeBuildInputs = [

View File

@@ -39,7 +39,7 @@ in
stdenv.mkDerivation (finalAttrs: {
pname = "lsp-plugins";
version = "1.2.27";
version = "1.2.29";
outputs = [
"out"
@@ -49,7 +49,7 @@ stdenv.mkDerivation (finalAttrs: {
src = fetchurl {
url = "https://github.com/lsp-plugins/lsp-plugins/releases/download/${finalAttrs.version}/lsp-plugins-src-${finalAttrs.version}.tar.gz";
hash = "sha256-AirMrXIb7ShPrURdzImss7nR8Scxkh2+HtvQzeZy4aM=";
hash = "sha256-QEKXw63YecUI2wbVEZ4R78HQRzUn0qae057HfB0ZcvI=";
};
# By default, GStreamer plugins are installed right alongside GStreamer itself

View File

@@ -30,11 +30,11 @@
stdenv.mkDerivation (finalAttrs: {
pname = "netatalk";
version = "4.4.2";
version = "4.4.3";
src = fetchurl {
url = "mirror://sourceforge/netatalk/netatalk/netatalk-${finalAttrs.version}.tar.xz";
hash = "sha256-Mfyjs3kBSBDkudakmK7ihtKdPp/3AybofhTZPqptCl4=";
hash = "sha256-hj1kDsyZ9JI+rWxY6NNAarOhyp3TsNR8zfb967bv46s=";
};
nativeBuildInputs = [

View File

@@ -67,16 +67,16 @@ let
in
buildGoModule (finalAttrs: {
pname = "netbird-${componentName}";
version = "0.70.4";
version = "0.70.5";
src = fetchFromGitHub {
owner = "netbirdio";
repo = "netbird";
tag = "v${finalAttrs.version}";
hash = "sha256-tfScscRllUlV1V6D66rfT6JEsReDQfVGryVzNebm0vg=";
hash = "sha256-AsM+MEeBqxnwD1jE8ocI93tF3l/7s+s5nF073ZMAi/Y=";
};
vendorHash = "sha256-IRV1GxdUKgan0GwmBg9acpl7plW01CtEO2FrKrlDdeE=";
vendorHash = "sha256-ebhjN6o/519ayxWTcscNinKuiL3LSPmE2VNgSitxj5g=";
nativeBuildInputs = [ installShellFiles ] ++ lib.optional (componentName == "ui") pkg-config;

View File

@@ -142,6 +142,7 @@ dependencies = [
"serde",
"serde_json",
"tempfile",
"xattr",
]
[[package]]
@@ -506,3 +507,13 @@ checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]
[[package]]
name = "xattr"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
dependencies = [
"libc",
"rustix",
]

View File

@@ -11,6 +11,7 @@ pathrs = "0.2.2"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.145"
bootspec = "2.0.0"
xattr = "1.6.1"
[dev-dependencies]
tempfile = "3.20.0"

View File

@@ -52,6 +52,9 @@ closure. Currently nixos-init comes in at ~500 KiB.
- `find-etc`: Finds the `/etc` paths in `/sysroot` so that the initrd doesn't
directly depend on the toplevel, reducing the need to rebuild the initrd on
every generation.
- `clear-etc-opaque`: Clears stale `trusted.overlay.opaque` xattrs from the
mutable `/etc` overlay's upperdir before it is mounted, so that lowerdir
entries added by a new generation are not hidden.
- `resolve-in-root`: Figures out the canonical path inside a chroot.
## Future

View File

@@ -47,6 +47,7 @@ rustPlatform.buildRustPackage (finalAttrs: {
binaries = [
"initrd-init"
"find-etc"
"clear-etc-opaque"
"resolve-in-root"
"env-generator"
];

View File

@@ -0,0 +1,170 @@
use std::{
env, fs,
path::{Path, PathBuf},
};
use anyhow::{Context, Result, bail};
const OVERLAY_OPAQUE_XATTR: &str = "trusted.overlay.opaque";
/// Entrypoint for the `clear-etc-opaque` binary.
///
/// When a directory is created in the mutable `/etc` overlay that does not yet
/// exist in the lowerdir, overlayfs marks it opaque in the upperdir. This is
/// correct at creation time, but becomes stale when a later generation adds
/// entries under that same directory to the metadata layer: the opaque marker
/// hides them.
///
/// This walks the (newly mounted) metadata layer and removes
/// `trusted.overlay.opaque` from any upperdir directory that now has a
/// directory counterpart in the lowerdir, turning it back into a merged view.
/// Files the user placed in the upperdir remain visible (upperdir wins
/// per-entry) and individual whiteouts are preserved; only the blanket hiding
/// of lowerdir content is undone.
///
/// See <https://github.com/NixOS/nixpkgs/issues/505475>.
///
/// Usage: `clear-etc-opaque <metadata-mount> <upperdir>`
pub fn clear_etc_opaque() -> Result<()> {
let args: Vec<String> = env::args().collect();
if args.len() != 3 {
bail!("Usage: {} <metadata-mount> <upperdir>", args[0]);
}
let metadata_mount = PathBuf::from(&args[1]);
let upperdir = PathBuf::from(&args[2]);
if !upperdir.is_dir() {
// Nothing to clear (e.g. first boot before the upperdir is created).
log::info!(
"Upperdir {} does not exist, nothing to clear.",
upperdir.display()
);
return Ok(());
}
clear_opaque_markers(&metadata_mount, &metadata_mount, &upperdir)
}
/// Recursively walk `current` (a subtree of `metadata_root`) and clear the
/// opaque xattr from the corresponding directory in `upperdir`.
fn clear_opaque_markers(metadata_root: &Path, current: &Path, upperdir: &Path) -> Result<()> {
let entries = fs::read_dir(current)
.with_context(|| format!("Failed to read directory {}", current.display()))?;
for entry in entries {
let entry =
entry.with_context(|| format!("Failed to read entry in {}", current.display()))?;
// Use the entry's own type info (no symlink following) so we only
// recurse into real directories of the metadata image.
if !entry
.file_type()
.with_context(|| format!("Failed to stat {}", entry.path().display()))?
.is_dir()
{
continue;
}
let path = entry.path();
let rel = path
.strip_prefix(metadata_root)
.context("Failed to strip metadata root prefix")?;
let target = upperdir.join(rel);
// Only act on real directories in the upperdir; an opaque marker on a
// non-directory would be meaningless and we must not follow symlinks
// out of the upperdir.
match fs::symlink_metadata(&target) {
Ok(meta) if meta.is_dir() => {
remove_opaque_xattr(&target);
// Only recurse when the upperdir also has this directory:
// deeper lowerdir directories without an upperdir counterpart
// cannot carry stale markers.
clear_opaque_markers(metadata_root, &path, upperdir)?;
}
// Missing or not a directory: nothing to do for this subtree.
_ => {}
}
}
Ok(())
}
/// Remove the `trusted.overlay.opaque` xattr from `path` if present.
fn remove_opaque_xattr(path: &Path) {
// Check first instead of removing unconditionally: lremovexattr(2) reports
// a missing attribute as ENODATA, which std does not map to a stable
// io::ErrorKind, so distinguishing it from real errors is awkward.
match xattr::get(path, OVERLAY_OPAQUE_XATTR) {
Ok(None) => return,
Ok(Some(_)) => {}
Err(err) => {
log::warn!(
"Failed to read {OVERLAY_OPAQUE_XATTR} on {}: {err}.",
path.display()
);
return;
}
}
match xattr::remove(path, OVERLAY_OPAQUE_XATTR) {
Ok(()) => {
log::info!("Cleared stale opaque marker from {}.", path.display());
}
Err(err) => {
// Don't abort the boot over this; the worst case is that some
// declaratively-managed /etc entries stay hidden, which is what
// would happen anyway without this fixup.
log::warn!(
"Failed to remove {OVERLAY_OPAQUE_XATTR} from {}: {err}.",
path.display()
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
#[test]
fn clears_opaque_only_for_matching_dirs() -> Result<()> {
if !xattr::SUPPORTED_PLATFORM {
return Ok(());
}
let metadata = tempdir()?;
let upper = tempdir()?;
// lowerdir gained nixos/sub in the new generation.
fs::create_dir_all(metadata.path().join("nixos/sub"))?;
// upperdir has an opaque nixos/ from before.
fs::create_dir_all(upper.path().join("nixos"))?;
// upperdir directory without a lowerdir counterpart: we only clear
// markers where the lowerdir has a matching directory, so this one
// must stay opaque.
fs::create_dir_all(upper.path().join("only-upper"))?;
// The build sandbox usually lacks CAP_SYS_ADMIN, so trusted.* xattrs
// cannot be set. Skip in that case rather than fail the build.
if xattr::set(upper.path().join("nixos"), OVERLAY_OPAQUE_XATTR, b"y").is_err() {
eprintln!("skipping: cannot set trusted.* xattrs in this environment");
return Ok(());
}
xattr::set(upper.path().join("only-upper"), OVERLAY_OPAQUE_XATTR, b"y")?;
clear_opaque_markers(metadata.path(), metadata.path(), upper.path())?;
assert!(xattr::get(upper.path().join("nixos"), OVERLAY_OPAQUE_XATTR)?.is_none());
assert_eq!(
xattr::get(upper.path().join("only-upper"), OVERLAY_OPAQUE_XATTR)?.as_deref(),
Some(b"y".as_slice())
);
Ok(())
}
}

View File

@@ -1,6 +1,7 @@
mod activate;
mod config;
mod env_generator;
mod etc_overlay;
mod find_etc;
mod fs;
mod init;
@@ -16,6 +17,7 @@ use anyhow::{Context, Result, bail};
pub use crate::{
activate::activate,
env_generator::env_generator,
etc_overlay::clear_etc_opaque,
find_etc::find_etc,
init::init,
initrd_init::initrd_init,

View File

@@ -2,7 +2,7 @@ use std::{env, io::Write, process::ExitCode};
use log::Level;
use nixos_init::{env_generator, find_etc, initrd_init, resolve_in_root};
use nixos_init::{clear_etc_opaque, env_generator, find_etc, initrd_init, resolve_in_root};
fn main() -> ExitCode {
let arg0 = env::args()
@@ -12,6 +12,7 @@ fn main() -> ExitCode {
setup_logger();
let entrypoint = match arg0.as_str() {
"clear-etc-opaque" => clear_etc_opaque,
"find-etc" => find_etc,
"resolve-in-root" => resolve_in_root,
"initrd-init" => initrd_init,

View File

@@ -11,6 +11,7 @@
cacert,
git,
nix,
nix-eval-jobs,
versionCheckHook,
withAutocomplete ? true,
@@ -55,6 +56,7 @@ python3Packages.buildPythonApplication (finalAttrs: {
let
binPath = [
nix
nix-eval-jobs
git
]
++ lib.optional withSandboxSupport bubblewrap

View File

@@ -6,16 +6,16 @@
}:
buildGoModule (finalAttrs: {
pname = "oh-my-posh";
version = "29.12.0";
version = "29.13.1";
src = fetchFromGitHub {
owner = "jandedobbeleer";
repo = "oh-my-posh";
tag = "v${finalAttrs.version}";
hash = "sha256-Gtv3h46+T3+hB4e2KJjS128RCNx/0wh/Qz52T+xxClI=";
hash = "sha256-mH727EMcIkORPEvI2OJRaGw+PAI4PRuUvwlVUfL67B8=";
};
vendorHash = "sha256-xDMSfuzREtn/Bm1rkRoUS3Ykec81WeA81VW6dp1wLb4=";
vendorHash = "sha256-cVn5QCpYc5FIY8hNaQnJ+eL0ZtVf6u2dWnWmHYJoJRk=";
sourceRoot = "${finalAttrs.src.name}/src";

View File

@@ -8,16 +8,16 @@
buildGoModule (finalAttrs: {
pname = "otel-tui";
version = "0.7.2";
version = "0.7.3";
src = fetchFromGitHub {
owner = "ymtdzzz";
repo = "otel-tui";
tag = "v${finalAttrs.version}";
hash = "sha256-teTV4i27Xjf5E1/2C1e8HFf6Tkct2wgZZfu9SSM18II=";
hash = "sha256-RYGSMdJQ2qj6930PX/UWFrN2orQzRpufHiKDv6lmAw4=";
};
vendorHash = "sha256-5ki/hR809LmMEHV8Mb7n2nEETAZR2Qs29AW5vvSzuu4=";
vendorHash = "sha256-dItC0l6BawbW2OCjg1KoDo8jHGX7Dbo2gSiVi1lynwI=";
env.GOWORK = "off";

View File

@@ -0,0 +1,28 @@
{
buildGoModule,
lib,
fetchFromGitHub,
}:
buildGoModule (finalAttrs: {
__structuredAttrs = true;
pname = "prometheus-speedtest-exporter";
version = "1.0.0";
src = fetchFromGitHub {
owner = "podocarp";
repo = "speedtest_exporter";
tag = "v${finalAttrs.version}";
hash = "sha256-n9eunZRssS13mTOeFeZ/PpfSj430DKf3ZRS10hY4Ps8=";
};
vendorHash = "sha256-HBg44D0CUc4HYCBwGrswnrqG5o5ltA6UT8L0oWetlIc=";
meta = {
description = "Speedtest.net Exporter for the Prometheus monitoring system";
mainProgram = "speedtest_exporter";
homepage = "https://github.com/podocarp/speedtest_exporter";
license = lib.licenses.gpl3Only;
maintainers = with lib.maintainers; [ podocarp ];
};
})

View File

@@ -7,14 +7,14 @@
python3.pkgs.buildPythonApplication (finalAttrs: {
pname = "radicale";
version = "3.7.2";
version = "3.7.3";
pyproject = true;
src = fetchFromGitHub {
owner = "Kozea";
repo = "Radicale";
tag = "v${finalAttrs.version}";
hash = "sha256-eZnm3mlG+HoqOXlKxv4UIdN99z04ty0qiwaJeNo3zEM=";
hash = "sha256-4xTfQ+E9ys0ox9AwQ/drQWLaA4YLZBBdyXUFUvBa8sk=";
};
build-system = with python3.pkgs; [

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "bypass.yazi";
version = "25.3.2-unstable-2025-06-01";
version = "0-unstable-2025-06-01";
src = fetchFromGitHub {
owner = "Rolv-Apneseth";

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "chmod.yazi";
version = "26.1.22-unstable-2026-02-27";
version = "0-unstable-2026-02-27";
src = fetchFromGitHub {
owner = "yazi-rs";

View File

@@ -5,13 +5,13 @@
}:
mkYaziPlugin {
pname = "clipboard.yazi";
version = "0-unstable-2026-04-24";
version = "0-unstable-2026-05-10";
src = fetchFromGitHub {
owner = "XYenon";
repo = "clipboard.yazi";
rev = "d6fc53152a20aebad8dc6e2550940f7efe226838";
hash = "sha256-6jlMzVPgkbQRwVbfUCEtXVWLxBKdPymQeHVoh5z9mO8=";
rev = "68b506d9a9c2c5dde01a078a589520f551d05fe5";
hash = "sha256-jNBwkcFb9i5Z6BSMfkTOyrK7HZohAT/yB3cxcCOG54w=";
};
meta = {

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "close-and-restore-tab.yazi";
version = "0-unstable-2026-04-22";
version = "0-unstable-2025-05-29";
src = fetchFromGitHub {
owner = "MasouShizuka";

View File

@@ -5,12 +5,12 @@
}:
mkYaziPlugin {
pname = "compress.yazi";
version = "25.12.29-unstable-2026-03-15";
version = "0.6";
src = fetchFromGitHub {
owner = "KKV9";
repo = "compress.yazi";
rev = "46a6b9f02ff2f8aced466a1f01a3fe241f1cd45f";
tag = "0.6";
hash = "sha256-Mby185FCJY6nqHcHDQu+D5SLk+wGcyeUHK8yAvrd4TM=";
};

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "convert.yazi";
version = "0-unstable-2026-04-22";
version = "0-unstable-2025-06-28";
src = fetchFromGitHub {
owner = "atareao";

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "diff.yazi";
version = "26.1.22-unstable-2026-01-24";
version = "0-unstable-2026-01-24";
src = fetchFromGitHub {
owner = "yazi-rs";

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "full-border.yazi";
version = "25.2.26-unstable-2026-04-22";
version = "0-unstable-2026-04-22";
src = fetchFromGitHub {
owner = "yazi-rs";

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "git.yazi";
version = "26.1.22-unstable-2026-02-27";
version = "0-unstable-2026-02-27";
src = fetchFromGitHub {
owner = "yazi-rs";

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "gvfs.yazi";
version = "25.5.31-unstable-2026-03-29";
version = "0-unstable-2026-03-29";
src = fetchFromGitHub {
owner = "boydaihungst";

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "jump-to-char.yazi";
version = "25.5.31-unstable-2025-06-18";
version = "0-unstable-2025-06-18";
src = fetchFromGitHub {
owner = "yazi-rs";

View File

@@ -5,13 +5,13 @@
}:
mkYaziPlugin {
pname = "lazygit.yazi";
version = "0-unstable-2026-03-12";
version = "0-unstable-2026-05-02";
src = fetchFromGitHub {
owner = "Lil-Dank";
repo = "lazygit.yazi";
rev = "8c4086c813c5856ab9571ae9142ed7d40ed3211e";
hash = "sha256-YpRWnR5fEXzHY9yBFNKy1NvTzHa8B1UhS2Qrfe9+Tpg=";
rev = "e73fd74c2af3300368b33da1cfbab6a8649a41a8";
hash = "sha256-KPvjXjYE0W4Q2xZiVfMwZbtalHt0FbgLtEK4sUWbYOI=";
};
meta = {

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "lsar.yazi";
version = "26.1.22-unstable-2026-01-24";
version = "0-unstable-2026-01-24";
src = fetchFromGitHub {
owner = "yazi-rs";

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "mactag.yazi";
version = "26.1.22-unstable-2026-04-10";
version = "0-unstable-2026-04-10";
src = fetchFromGitHub {
owner = "yazi-rs";

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "mediainfo.yazi";
version = "26.1.22-unstable-2026-04-10";
version = "0-unstable-2026-04-10";
src = fetchFromGitHub {
owner = "boydaihungst";

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "mount.yazi";
version = "25.12.29-unstable-2026-04-09";
version = "0-unstable-2026-04-09";
src = fetchFromGitHub {
owner = "yazi-rs";

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "office.yazi";
version = "0-unstable-2026-04-22";
version = "0-unstable-2025-09-20";
src = fetchFromGitHub {
owner = "macydnah";

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "piper.yazi";
version = "26.1.22-unstable-2026-01-24";
version = "0-unstable-2026-01-24";
src = fetchFromGitHub {
owner = "yazi-rs";

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "relative-motions.yazi";
version = "25.5.28-unstable-2025-07-09";
version = "0-unstable-2025-07-09";
src = fetchFromGitHub {
owner = "dedukun";

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "restore.yazi";
version = "25.5.31-unstable-2026-04-04";
version = "0-unstable-2026-04-04";
src = fetchFromGitHub {
owner = "boydaihungst";

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "smart-enter.yazi";
version = "25.5.31-unstable-2025-06-18";
version = "0-unstable-2025-06-18";
src = fetchFromGitHub {
owner = "yazi-rs";

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "smart-filter.yazi";
version = "25.12.29-unstable-2025-12-29";
version = "0-unstable-2025-12-29";
src = fetchFromGitHub {
owner = "yazi-rs";

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "smart-paste.yazi";
version = "25.5.31-unstable-2025-06-18";
version = "0-unstable-2025-06-18";
src = fetchFromGitHub {
owner = "yazi-rs";

View File

@@ -6,13 +6,13 @@
mkYaziPlugin {
pname = "split-tabs.yazi";
version = "0-unstable-2026-04-13";
version = "0-unstable-2026-05-13";
src = fetchFromGitHub {
owner = "terrakok";
repo = "split-tabs.yazi";
rev = "6c0931840d764bffa0c38677b6a84e69928e283f";
hash = "sha256-FqeXVVFk4+aXn8d+LLs8idRBkLLzRPeVol6vMCh6mQ4=";
rev = "ca95efc94a3a62e6e58c741f60801c1a0ddba1a6";
hash = "sha256-ic09opWZcoJ874bU2HN+5Y9mbnZEnvds+abqRQYuiYE=";
};
meta = {

View File

@@ -5,13 +5,13 @@
}:
mkYaziPlugin {
pname = "sshfs.yazi";
version = "2.0.0-unstable-2026-04-15";
version = "2.1.0";
src = fetchFromGitHub {
owner = "uhs-robert";
repo = "sshfs.yazi";
rev = "7ba17a8c8498fca9f0a9c437704e74b56d96ed96";
hash = "sha256-TS3/xl8jbbCoF1LzPYvmG9BRqvlzPg4EZRErlL7S2/M=";
tag = "v2.1.0";
hash = "sha256-02LzKNptzs6o+YPGJRyYCly/Xqzi/5mvVBS+b28nY6U=";
};
meta = {

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "starship.yazi";
version = "25.4.8-unstable-2026-03-22";
version = "0-unstable-2026-03-22";
src = fetchFromGitHub {
owner = "Rolv-Apneseth";

View File

@@ -5,13 +5,13 @@
}:
mkYaziPlugin {
pname = "sudo.yazi";
version = "0-unstable-2025-11-05";
version = "0-unstable-2026-05-07";
src = fetchFromGitHub {
owner = "TD-Sky";
repo = "sudo.yazi";
rev = "86205aa8044f10b02471be1087f3381bbadc967e";
hash = "sha256-mpQLij+Sg88RarCC+0u7JfZ2EqcX4gB7jvy8bfBt90w=";
rev = "afd61cedbcd13c549c552766755645069561d28c";
hash = "sha256-5wa4fdYo7wOXOzdrigWMtADZIL/7alG8Jw7r8iWz9yA=";
};
meta = {

View File

@@ -5,13 +5,13 @@
}:
mkYaziPlugin {
pname = "toggle-pane.yazi";
version = "25.5.31-unstable-2025-06-18";
version = "0-unstable-2026-05-07";
src = fetchFromGitHub {
owner = "yazi-rs";
repo = "plugins";
rev = "86d28e4fb4f25f36cc501b8cb0badb37a6b14263";
hash = "sha256-m/gJTDm0cVkIdcQ1ZJliPqBhNKoCW1FciLkuq7D1mxo=";
rev = "4ffa48f33465c22cce48c5d506295a3eb27c1979";
hash = "sha256-wr5QL493A175dRjYSyYpMMJax1RKWaZ3jAdFdL3XXTw=";
};
meta = {

View File

@@ -1,16 +1,32 @@
#!/usr/bin/env nix-shell
#!nix-shell -i python3 -p python3 python3Packages.requests python3Packages.packaging nix curl git argparse
#!nix-shell -i python3 -p python3 python3Packages.requests python3Packages.packaging python3Packages.nixpkgs-plugin-update nix curl git argparse
import argparse
import base64
import json
import os
import re
import subprocess
import sys
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
import requests
from nixpkgs_plugin_update import make_unstable_version, normalize_release_version
from packaging import version
from packaging.version import InvalidVersion
@dataclass
class UpdateCandidate:
"""An upstream revision that might be used to update a plugin."""
rev: str
commit: str | None
date: str
version: str
kind: str
def run_command(cmd: str, capture_output: bool = True) -> str:
@@ -65,18 +81,42 @@ def get_default_branch(owner: str, repo: str, headers: dict[str, str]) -> str:
print("Falling back to 'main' as default branch")
return "main"
def fetch_plugin_content(owner: str, repo: str, plugin_pname: str, headers: dict[str, str]) -> str:
"""Fetch the plugin's main.lua content from GitHub"""
default_branch = get_default_branch(owner, repo, headers)
plugin_path = f"{plugin_pname}/" if owner == "yazi-rs" else ""
main_lua_url = f"https://raw.githubusercontent.com/{owner}/{repo}/{default_branch}/{plugin_path}main.lua"
def github_get(
owner: str,
repo: str,
path: str,
headers: dict[str, str],
params: dict[str, str | int] | None = None,
allow_404: bool = False,
) -> dict | list | None:
"""Fetch JSON from the GitHub API."""
api_url = f"https://api.github.com/repos/{owner}/{repo}/{path}"
try:
response = requests.get(main_lua_url, headers=headers)
response = requests.get(api_url, headers=headers, params=params)
if allow_404 and response.status_code == 404:
return None
response.raise_for_status()
return response.text
return response.json()
except requests.RequestException as e:
raise RuntimeError(f"Error fetching plugin content: {e}")
raise RuntimeError(f"Error fetching {api_url}: {e}")
def fetch_plugin_content(
owner: str,
repo: str,
plugin_pname: str,
ref: str,
headers: dict[str, str],
) -> str:
"""Fetch the plugin's main.lua content from GitHub"""
plugin_path = f"{plugin_pname}/" if owner == "yazi-rs" else ""
content_data = github_get(owner, repo, f"contents/{plugin_path}main.lua", headers, {"ref": ref})
if not isinstance(content_data, dict) or "content" not in content_data:
raise RuntimeError(f"Could not fetch main.lua at {ref}")
return base64.b64decode(content_data["content"]).decode("utf-8")
def check_version_compatibility(plugin_content: str, plugin_name: str, yazi_version: str) -> str:
@@ -95,40 +135,241 @@ def check_version_compatibility(plugin_content: str, plugin_name: str, yazi_vers
return required_version
def get_latest_commit(owner: str, repo: str, plugin_pname: str, headers: dict[str, str]) -> tuple[str, str]:
"""Get the latest commit hash and date for the plugin"""
def candidate_sort_key(candidate: UpdateCandidate) -> tuple[int, version.Version | str]:
"""Sort candidates by parsed version when possible, then by text."""
try:
return (1, version.parse(candidate.version))
except InvalidVersion:
return (0, candidate.version)
def normalize_candidate(tag: str | None, kind: str) -> UpdateCandidate | None:
"""Create an unresolved release/tag candidate from a GitHub tag."""
candidate_version = normalize_release_version(tag) if tag else None
if candidate_version is None:
return None
return UpdateCandidate(
rev=tag,
commit=None,
date="unknown",
version=candidate_version,
kind=kind,
)
def resolve_candidate(owner: str, repo: str, candidate: UpdateCandidate, headers: dict[str, str]) -> UpdateCandidate:
"""Resolve a release/tag candidate to the commit it points at."""
if candidate.commit is not None:
return candidate
commit_data = github_get(owner, repo, f"commits/{candidate.rev}", headers)
if not isinstance(commit_data, dict):
raise RuntimeError(f"Could not resolve {candidate.rev}")
return UpdateCandidate(
rev=candidate.rev,
commit=commit_data["sha"],
date=commit_data["commit"]["committer"]["date"].split("T")[0],
version=candidate.version,
kind=candidate.kind,
)
def get_commit_candidates(owner: str, repo: str, plugin_pname: str, headers: dict[str, str]) -> list[UpdateCandidate]:
"""Get recent default branch commit candidates for a plugin."""
default_branch = get_default_branch(owner, repo, headers)
if owner == "yazi-rs":
# For official plugins, get commit info for the specific plugin file
api_url = f"https://api.github.com/repos/{owner}/{repo}/commits?path={plugin_pname}/main.lua&per_page=1"
commits_data = github_get(
owner,
repo,
"commits",
headers,
{"path": f"{plugin_pname}/main.lua", "per_page": 100},
)
if not isinstance(commits_data, list) or not commits_data:
raise RuntimeError(f"Could not get recent commits for {plugin_pname}")
else:
# For third-party plugins, get latest commit on default branch
api_url = f"https://api.github.com/repos/{owner}/{repo}/commits/{default_branch}"
commits_data = github_get(
owner,
repo,
"commits",
headers,
{"sha": default_branch, "per_page": 100},
)
if not isinstance(commits_data, list) or not commits_data:
raise RuntimeError(f"Could not get recent commits for {owner}/{repo}")
try:
response = requests.get(api_url, headers=headers)
response.raise_for_status()
commit_data = response.json()
except requests.RequestException as e:
raise RuntimeError(f"Error fetching commit data: {e}")
if owner == "yazi-rs":
latest_commit = commit_data[0]["sha"]
commit_date = commit_data[0]["commit"]["committer"]["date"].split("T")[0]
else:
latest_commit = commit_data["sha"]
candidates = []
for commit_data in commits_data:
commit = commit_data["sha"]
commit_date = commit_data["commit"]["committer"]["date"].split("T")[0]
commit_datetime = datetime.strptime(commit_date, "%Y-%m-%d")
if not latest_commit:
raise RuntimeError("Could not get latest commit hash")
candidates.append(
UpdateCandidate(
rev=commit,
commit=commit,
date=commit_date,
version=make_unstable_version(commit_datetime, None),
kind="commit",
)
)
return latest_commit, commit_date
return candidates
def calculate_sri_hash(owner: str, repo: str, latest_commit: str) -> str:
def get_release_candidates(owner: str, repo: str, headers: dict[str, str]) -> list[UpdateCandidate]:
"""Get non-prerelease GitHub release candidates without resolving tags."""
releases = github_get(owner, repo, "releases", headers, {"per_page": 100})
if not isinstance(releases, list):
return []
candidates = []
for release in releases:
if release.get("draft") or release.get("prerelease"):
continue
candidate = normalize_candidate(release.get("tag_name"), "release")
if candidate is not None:
candidates.append(candidate)
candidates.sort(key=candidate_sort_key, reverse=True)
return candidates
def get_tag_candidates(owner: str, repo: str, headers: dict[str, str]) -> list[UpdateCandidate]:
"""Get GitHub tag candidates without resolving tags."""
tags = github_get(owner, repo, "tags", headers, {"per_page": 100})
if not isinstance(tags, list):
return []
candidates = []
for tag in tags:
candidate = normalize_candidate(tag.get("name"), "tag")
if candidate is not None:
candidates.append(candidate)
candidates.sort(key=candidate_sort_key, reverse=True)
return candidates
def get_update_candidates(owner: str, repo: str, plugin_pname: str, headers: dict[str, str]) -> list[UpdateCandidate]:
"""Get update candidates, preferring releases, then tags, then commits."""
release_candidates = get_release_candidates(owner, repo, headers)
if release_candidates:
return release_candidates
tag_candidates = get_tag_candidates(owner, repo, headers)
if tag_candidates:
return tag_candidates
return get_commit_candidates(owner, repo, plugin_pname, headers)
def compare_to_current(
owner: str,
repo: str,
old_commit: str,
candidate: UpdateCandidate,
headers: dict[str, str],
) -> str:
"""Compare a candidate to the packaged commit."""
if old_commit == "unknown":
print("Current commit is unknown, skipping to avoid a possible regression")
return "unknown"
if candidate.commit is None:
raise RuntimeError(f"Candidate {candidate.rev} is unresolved")
if old_commit == candidate.commit or old_commit == candidate.rev:
return "identical"
compare_data = github_get(owner, repo, f"compare/{old_commit}...{candidate.commit}", headers, allow_404=True)
if not isinstance(compare_data, dict):
print(f"Could not compare {old_commit}...{candidate.commit}, skipping to avoid a possible regression")
return "unknown"
return str(compare_data.get("status"))
def is_yazi_compatible(
owner: str,
repo: str,
plugin_name: str,
plugin_pname: str,
candidate: UpdateCandidate,
yazi_version: str,
headers: dict[str, str],
) -> bool:
"""Check if a candidate supports nixpkgs' Yazi version."""
try:
plugin_content = fetch_plugin_content(owner, repo, plugin_pname, candidate.rev, headers)
check_version_compatibility(plugin_content, plugin_name, yazi_version)
return True
except RuntimeError as e:
print(f"Skipping {candidate.rev}: {e}")
return False
def select_compatible_candidate(
owner: str,
repo: str,
plugin_name: str,
plugin_pname: str,
old_ref_attr: str,
old_ref: str,
old_commit: str,
old_version: str,
yazi_version: str,
headers: dict[str, str],
) -> UpdateCandidate | None:
"""Select the newest compatible candidate that moves the package forward."""
candidates = get_update_candidates(owner, repo, plugin_pname, headers)
for unresolved_candidate in candidates:
try:
candidate = resolve_candidate(owner, repo, unresolved_candidate, headers)
except RuntimeError as e:
print(f"Skipping {unresolved_candidate.rev}: {e}")
if unresolved_candidate.kind in ("release", "tag"):
break
continue
print(f"Checking {plugin_name} {candidate.kind} {candidate.rev} ({candidate.date})")
compare_status = compare_to_current(owner, repo, old_commit, candidate, headers)
if compare_status == "identical":
candidate_ref_attr = "rev" if candidate.kind == "commit" else "tag"
source_ref_changed = old_ref_attr != candidate_ref_attr or old_ref != candidate.rev
if old_version != candidate.version or source_ref_changed:
if not is_yazi_compatible(owner, repo, plugin_name, plugin_pname, candidate, yazi_version, headers):
break
return candidate
print(f"{plugin_name} is already at {candidate.rev}; older candidates will not be used")
break
if compare_status != "ahead":
print(f"Skipping {candidate.rev}: compare status is {compare_status}, not ahead")
if candidate.kind in ("release", "tag"):
print(f"{candidate.rev} is not newer than the packaged revision; older {candidate.kind}s will not be used")
break
continue
if not is_yazi_compatible(owner, repo, plugin_name, plugin_pname, candidate, yazi_version, headers):
continue
return candidate
return None
def calculate_sri_hash(owner: str, repo: str, rev: str) -> str:
"""Calculate the SRI hash for the plugin source"""
prefetch_url = f"https://github.com/{owner}/{repo}/archive/{latest_commit}.tar.gz"
prefetch_url = f"https://github.com/{owner}/{repo}/archive/{rev}.tar.gz"
try:
new_hash = run_command(f"nix-prefetch-url --unpack --type sha256 {prefetch_url} 2>/dev/null")
@@ -167,16 +408,39 @@ def write_nix_file(file_path: str, content: str) -> None:
raise RuntimeError(f"Error writing to file {file_path}: {e}")
def update_nix_file(default_nix_path: str, latest_commit: str, new_version: str, new_hash: str) -> None:
def get_source_ref(nix_content: str) -> tuple[str, str]:
"""Get the source ref attribute from a Nix file."""
source_ref_match = re.search(r'\b(rev|tag) = "([^"]*)"', nix_content)
if source_ref_match is None:
raise RuntimeError("Could not find rev or tag attribute")
return source_ref_match.group(1), source_ref_match.group(2)
def resolve_ref_commit(owner: str, repo: str, ref: str, headers: dict[str, str]) -> str:
"""Resolve a GitHub ref to its commit."""
commit_data = github_get(owner, repo, f"commits/{ref}", headers)
if not isinstance(commit_data, dict):
raise RuntimeError(f"Could not resolve {ref}")
return commit_data["sha"]
def update_nix_file(default_nix_path: str, candidate: UpdateCandidate, new_hash: str) -> None:
"""Update the default.nix file with new version, revision and hash"""
default_nix_content = read_nix_file(default_nix_path)
default_nix_content = re.sub(r'rev = "[^"]*"', f'rev = "{latest_commit}"', default_nix_content)
source_ref_attr = "rev" if candidate.kind == "commit" else "tag"
default_nix_content = re.sub(
r'\b(rev|tag) = "[^"]*"',
f'{source_ref_attr} = "{candidate.rev}"',
default_nix_content,
)
if 'version = "' in default_nix_content:
default_nix_content = re.sub(r'version = "[^"]*"', f'version = "{new_version}"', default_nix_content)
default_nix_content = re.sub(r'version = "[^"]*"', f'version = "{candidate.version}"', default_nix_content)
else:
default_nix_content = re.sub(r'(pname = "[^"]*";)', f'\\1\n version = "{new_version}";', default_nix_content)
default_nix_content = re.sub(r'(pname = "[^"]*";)', f'\\1\n version = "{candidate.version}";', default_nix_content)
if 'hash = "' in default_nix_content:
default_nix_content = re.sub(r'hash = "[^"]*"', f'hash = "{new_hash}"', default_nix_content)
@@ -202,6 +466,11 @@ def get_all_plugins(nixpkgs_dir: str) -> list[dict[str, str]]:
excluded_attrs = ["mkYaziPlugin", "override", "overrideDerivation", "overrideAttrs", "recurseForDerivations"]
plugin_names = [name for name in plugin_names if name not in excluded_attrs]
plugin_names = [
name
for name in plugin_names
if Path(nixpkgs_dir, "pkgs/by-name/ya/yazi/plugins", name, "default.nix").exists()
]
plugins = []
for name in plugin_names:
@@ -247,8 +516,7 @@ def update_single_plugin(nixpkgs_dir: str, plugin_name: str, plugin_pname: str)
nix_content = read_nix_file(default_nix_path)
old_version_match = re.search(r'version = "([^"]*)"', nix_content)
old_version = old_version_match.group(1) if old_version_match else "unknown"
old_commit_match = re.search(r'rev = "([^"]*)"', nix_content)
old_commit = old_commit_match.group(1) if old_commit_match else "unknown"
old_ref_attr, old_ref = get_source_ref(nix_content)
plugin_info = get_plugin_info(nixpkgs_dir, plugin_name)
owner = plugin_info["owner"]
@@ -257,34 +525,43 @@ def update_single_plugin(nixpkgs_dir: str, plugin_name: str, plugin_pname: str)
yazi_version = get_yazi_version(nixpkgs_dir)
headers = get_github_headers()
try:
old_commit = old_ref if old_ref_attr == "rev" else resolve_ref_commit(owner, repo, old_ref, headers)
except RuntimeError as e:
print(f"Could not resolve current {old_ref_attr} {old_ref}: {e}")
old_commit = "unknown"
plugin_content = fetch_plugin_content(owner, repo, plugin_pname, headers)
required_version = check_version_compatibility(plugin_content, plugin_name, yazi_version)
latest_commit, commit_date = get_latest_commit(owner, repo, plugin_pname, headers)
print(f"Checking {plugin_name} latest commit {latest_commit} ({commit_date})")
if latest_commit == old_commit:
print(f"No changes for {plugin_name}, already at latest commit {latest_commit}")
candidate = select_compatible_candidate(
owner,
repo,
plugin_name,
plugin_pname,
old_ref_attr,
old_ref,
old_commit,
old_version,
yazi_version,
headers,
)
if candidate is None:
print(f"No forward compatible update found for {plugin_name}")
return None
print(f"Updating {plugin_name} from commit {old_commit} to {latest_commit}")
print(f"Updating {plugin_name} from {old_commit} to {candidate.rev}")
new_version = f"{required_version}-unstable-{commit_date}"
new_hash = calculate_sri_hash(owner, repo, latest_commit)
new_hash = calculate_sri_hash(owner, repo, candidate.rev)
print(f"Generated SRI hash: {new_hash}")
update_nix_file(default_nix_path, latest_commit, new_version, new_hash)
update_nix_file(default_nix_path, candidate, new_hash)
print(f"Successfully updated {plugin_name} from {old_version} to {new_version}")
print(f"Successfully updated {plugin_name} from {old_version} to {candidate.version}")
return {
"name": plugin_name,
"old_version": old_version,
"new_version": new_version,
"new_version": candidate.version,
"old_commit": old_commit,
"new_commit": latest_commit,
"new_commit": candidate.commit,
"owner": owner,
"repo": repo,
}

View File

@@ -5,7 +5,7 @@
}:
mkYaziPlugin {
pname = "vcs-files.yazi";
version = "26.1.22-unstable-2026-04-10";
version = "0-unstable-2026-04-10";
src = fetchFromGitHub {
owner = "yazi-rs";

View File

@@ -5,13 +5,13 @@
}:
mkYaziPlugin {
pname = "yafg.yazi";
version = "0-unstable-2026-01-10";
version = "0-unstable-2026-05-06";
src = fetchFromGitHub {
owner = "XYenon";
repo = "yafg.yazi";
rev = "dd03b133d6cd1ff92368360558da193517169f9e";
hash = "sha256-xTZ+6KRr85A4QpPWAE9QN1AnUVnCw/tvRvsWOmmayao=";
rev = "e6ba85125bfa4e3a60ef28b70949299712103b2a";
hash = "sha256-IKQscTTirtfbsXKzCmaokPDrQZqXa4MSY2+6DbEQluU=";
};
meta = {

View File

@@ -5,12 +5,12 @@
}:
mkYaziPlugin {
pname = "yatline.yazi";
version = "25.12.29-unstable-2026-01-27";
version = "0.5.0";
src = fetchFromGitHub {
owner = "imsi32";
repo = "yatline.yazi";
rev = "c5d4b487d6277dd68ea9d3c6537641bf4ae9cf8e";
tag = "v0.5.0";
hash = "sha256-HjTRAfUHs6vlEWKruQWeA2wT/Mcd+WEHM90egFTYcWQ=";
};

View File

@@ -30,8 +30,8 @@ buildPythonPackage rec {
patches = [
# Fix time-of-day-dependent failure in series_time validation.
(fetchpatch2 {
url = "https://github.com/ImagingDataCommons/highdicom/commit/e9e3f2514a74b0d4be736cff222c934ef66d67ff.patch";
hash = "sha256-48dJAimxXYG0FQouquY5TLXi+3HarS8yx9HoLXiFymM=";
url = "https://github.com/ImagingDataCommons/highdicom/commit/e9e3f2514a74b0d4be736cff222c934ef66d67ff.patch?full_index=1";
hash = "sha256-1h9xmcezxuvHw54t4kLahDB62d0XHzEyrmHmPf6NW7M=";
})
];

View File

@@ -5,6 +5,7 @@
pytestCheckHook,
fetchFromGitHub,
fetchpatch,
fetchpatch2,
replaceVars,
exiftool,
ffmpeg,
@@ -55,6 +56,16 @@ buildPythonPackage rec {
url = "https://github.com/jvoisin/mat2/commit/00b4f110711754496932c59d5af3c0b2ed694484.patch";
hash = "sha256-5h/nM1dK8HmYtoIBVGOvUegMFBpGxcfpn5O6QrjLi9M=";
})
# Loosen test_climat2 assertions split across terminal-width boundaries.
(fetchpatch2 {
url = "https://github.com/jvoisin/mat2/commit/690e01d475117a4e0c85f26154b26ef332f036be.patch?full_index=1";
hash = "sha256-pDbY3E6BPp20orDOx7zxhCdAB+nAdpddTYjPYHStVLc=";
})
# Fix test_climat2 under Python 3.14+ argparse usage formatting.
(fetchpatch2 {
url = "https://github.com/jvoisin/mat2/commit/05f34a17695be65b1ad9782911f87e000de8fc8b.patch?full_index=1";
hash = "sha256-rIaXocT+LKM2De5iBPIPoBdFbdd17TJJPyFrPzNAJF0=";
})
];
postPatch = ''

View File

@@ -5,11 +5,11 @@
}:
mkKdeDerivation rec {
pname = "plasma-wayland-protocols";
version = "1.20.0";
version = "1.21.0";
src = fetchurl {
url = "mirror://kde/stable/plasma-wayland-protocols/plasma-wayland-protocols-${version}.tar.xz";
hash = "sha256-mBi7FGIhHOWYLmcKvw2WTrEf4dDAKhwmCE2zBpWnnWo=";
hash = "sha256-aYp7KLcRJwMU45biSK6GCHz+rtATcgCQY5lb5uHchbo=";
};
meta.license = with lib.licenses; [

View File

@@ -25,18 +25,18 @@
"lts": true
},
"6.12": {
"version": "6.12.87",
"hash": "sha256:0c4qidff0qs2x0mvba83cw3ksaz2af3xwabvc839xvsc9djaf4nc",
"version": "6.12.88",
"hash": "sha256:1s2x66j46gxw17j8hh5iws0l00aivmphp3mn8vgwbn8sj44gacjb",
"lts": true
},
"6.18": {
"version": "6.18.29",
"hash": "sha256:0g584ak8p9nqxysn8d1qzxbp1asfd39hqy9np35gri9v3xdsfgn3",
"version": "6.18.30",
"hash": "sha256:1m5kvzky4g2jc8b09np8can0lasg1lwjfgdj41s3ymxgqdp8icx8",
"lts": true
},
"7.0": {
"version": "7.0.6",
"hash": "sha256:08vm18wx6399phzgr3wz94yga3ab4fyca79445ygvbspm904996b",
"version": "7.0.7",
"hash": "sha256:1x2xnb7gpj0inxdc317zi71i0d98b7wq64s0yzk2vzxalf3gxqf8",
"lts": false
}
}

View File

@@ -1,6 +1,7 @@
{
lib,
mkMesonExecutable,
stdenv,
nix-store,
nix-expr,
@@ -12,6 +13,11 @@
# Configuration Options
version,
# Whether to link against mimalloc for malloc override.
# Significantly improves evaluation performance on allocation-heavy
# workloads (~10-15% on large evaluations).
withMimalloc ? !stdenv.hostPlatform.isWindows,
}:
mkMesonExecutable (finalAttrs: {
@@ -25,10 +31,11 @@ mkMesonExecutable (finalAttrs: {
nix-expr
nix-main
nix-cmd
mimalloc
];
]
++ lib.optional ((lib.versionAtLeast version "2.35pre") && withMimalloc) mimalloc;
mesonFlags = [
mesonFlags = lib.optionals (lib.versionAtLeast version "2.35pre") [
(lib.mesonEnable "mimalloc" withMimalloc)
];
meta = {

View File

@@ -56,6 +56,7 @@ let
"communicator"
"dolphin-plugins"
"eventviews"
"extra-cmake-modules"
"flatpak-kcm"
"ghostwriter"
"grantleetheme"