throne: init at 1.0.8-unstable-2025-10-29 (renamed from nekoray); nixos/throne: rename from nekoray (#430949)
This commit is contained in:
@@ -269,7 +269,6 @@
|
||||
./programs/nano.nix
|
||||
./programs/nautilus-open-any-terminal.nix
|
||||
./programs/nbd.nix
|
||||
./programs/nekoray.nix
|
||||
./programs/neovim.nix
|
||||
./programs/nethoscope.nix
|
||||
./programs/nexttrace.nix
|
||||
@@ -327,6 +326,7 @@
|
||||
./programs/systemtap.nix
|
||||
./programs/tcpdump.nix
|
||||
./programs/television.nix
|
||||
./programs/throne.nix
|
||||
./programs/thunar.nix
|
||||
./programs/thunderbird.nix
|
||||
./programs/tmux.nix
|
||||
|
||||
@@ -6,20 +6,24 @@
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.programs.nekoray;
|
||||
cfg = config.programs.throne;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
programs.nekoray = {
|
||||
enable = lib.mkEnableOption "nekoray, a GUI proxy configuration manager";
|
||||
imports = [
|
||||
(lib.mkRenamedOptionModule [ "programs" "nekoray" ] [ "programs" "throne" ])
|
||||
];
|
||||
|
||||
package = lib.mkPackageOption pkgs "nekoray" { };
|
||||
options = {
|
||||
programs.throne = {
|
||||
enable = lib.mkEnableOption "Throne, a GUI proxy configuration manager";
|
||||
|
||||
package = lib.mkPackageOption pkgs "throne" { };
|
||||
|
||||
tunMode = {
|
||||
enable = lib.mkEnableOption "TUN mode of nekoray";
|
||||
enable = lib.mkEnableOption "TUN mode of Throne";
|
||||
|
||||
setuid = lib.mkEnableOption ''
|
||||
setting suid bit for nekobox_core to run as root, which is less
|
||||
setting suid bit for throne-core to run as root, which is less
|
||||
secure than default setcap method but closer to upstream assumptions.
|
||||
Enable this if you find the default setcap method configured in
|
||||
this module doesn't work for you
|
||||
@@ -29,10 +33,11 @@ in
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
|
||||
security.wrappers.nekobox_core = lib.mkIf cfg.tunMode.enable {
|
||||
source = "${cfg.package}/share/nekoray/nekobox_core";
|
||||
security.wrappers.throne-core = lib.mkIf cfg.tunMode.enable {
|
||||
source = "${cfg.package}/share/throne/Core";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
setuid = lib.mkIf cfg.tunMode.setuid true;
|
||||
@@ -44,7 +49,7 @@ in
|
||||
|
||||
# avoid resolvectl password prompt popping up three times
|
||||
# https://github.com/SagerNet/sing-tun/blob/0686f8c4f210f4e7039c352d42d762252f9d9cf5/tun_linux.go#L1062
|
||||
# We use a hack here to determine whether the requested process is nekobox_core
|
||||
# We use a hack here to determine whether the requested process is throne-core
|
||||
# Detect whether its capabilities contain at least `net_admin` and `net_raw`.
|
||||
# This does not reduce security, as we can already bypass `resolved` with them.
|
||||
# Alternatives to consider:
|
||||
@@ -56,9 +61,9 @@ in
|
||||
# change its own cmdline. `/proc/<pid>/exe` is reliable but kernel forbids
|
||||
# checking that entry of process from different users, and polkit runs `spawn`
|
||||
# as an unprivileged user.
|
||||
# 3. Put nekobox_core into a systemd service, and let polkit check service name.
|
||||
# 3. Put throne-core into a systemd service, and let polkit check service name.
|
||||
# This is the most secure and convenient way but requires heavy modification
|
||||
# to nekoray source code. Would be good to let upstream support that eventually.
|
||||
# to Throne source code. Would be good to let upstream support that eventually.
|
||||
security.polkit.extraConfig =
|
||||
lib.mkIf (cfg.tunMode.enable && (!cfg.tunMode.setuid) && config.services.resolved.enable)
|
||||
''
|
||||
@@ -1,149 +0,0 @@
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
|
||||
buildGoModule,
|
||||
fetchFromGitHub,
|
||||
fetchpatch,
|
||||
makeDesktopItem,
|
||||
|
||||
cmake,
|
||||
copyDesktopItems,
|
||||
ninja,
|
||||
|
||||
protobuf,
|
||||
qt6Packages,
|
||||
|
||||
sing-geoip,
|
||||
sing-geosite,
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "nekoray";
|
||||
version = "4.3.7";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "Mahdi-zarei";
|
||||
repo = "nekoray";
|
||||
tag = finalAttrs.version;
|
||||
hash = "sha256-oRoHu9mt4LiGJFe2OEATbPQ8buYT/6o9395BxYg1qKI=";
|
||||
};
|
||||
|
||||
strictDeps = true;
|
||||
|
||||
nativeBuildInputs = [
|
||||
cmake
|
||||
copyDesktopItems
|
||||
ninja
|
||||
qt6Packages.wrapQtAppsHook
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
protobuf
|
||||
qt6Packages.qtbase
|
||||
qt6Packages.qttools
|
||||
];
|
||||
|
||||
cmakeFlags = [
|
||||
# makes sure the app uses the user's config directory to store it's non-static content
|
||||
# it's essentially the same as always setting the -appdata flag when running the program
|
||||
(lib.cmakeBool "NKR_PACKAGE" true)
|
||||
];
|
||||
|
||||
patches = [
|
||||
# if compiled with NKR_PACKAGE, nekoray assumes geoip.db and geosite.db will be found in ~/.config/nekoray
|
||||
# we already package those two files in nixpkgs
|
||||
# we can't place file at that location using our builder so we must change the search directory to be relative to the built executable
|
||||
./search-for-geodata-in-install-location.patch
|
||||
|
||||
# disable suid request as it cannot be applied to nekobox_core in nix store
|
||||
# and prompt users to use NixOS module instead. And use nekobox_core from PATH
|
||||
# to make use of security wrappers
|
||||
./nixos-disable-setuid-request.patch
|
||||
];
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
install -Dm755 nekoray "$out/share/nekoray/nekoray"
|
||||
install -Dm644 "$src/res/public/nekobox.png" "$out/share/icons/hicolor/256x256/apps/nekoray.png"
|
||||
|
||||
mkdir -p "$out/bin"
|
||||
ln -s "$out/share/nekoray/nekoray" "$out/bin"
|
||||
|
||||
# nekoray looks for other files and cores in the same directory it's located at
|
||||
ln -s ${finalAttrs.passthru.nekobox-core}/bin/nekobox_core "$out/share/nekoray/nekobox_core"
|
||||
|
||||
# our patch also makes nekoray look for geodata files next to the executable
|
||||
ln -s ${sing-geoip}/share/sing-box/geoip.db "$out/share/nekoray/geoip.db"
|
||||
ln -s ${sing-geosite}/share/sing-box/geosite.db "$out/share/nekoray/geosite.db"
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
desktopItems = [
|
||||
(makeDesktopItem {
|
||||
name = "nekoray";
|
||||
desktopName = "Nekoray";
|
||||
exec = "nekoray";
|
||||
icon = "nekoray";
|
||||
comment = finalAttrs.meta.description;
|
||||
terminal = false;
|
||||
categories = [ "Network" ];
|
||||
})
|
||||
];
|
||||
|
||||
passthru.nekobox-core = buildGoModule {
|
||||
pname = "nekobox-core";
|
||||
inherit (finalAttrs) version src;
|
||||
sourceRoot = "${finalAttrs.src.name}/core/server";
|
||||
|
||||
patches = [
|
||||
# also check cap_net_admin so we don't have to set suid
|
||||
./core-also-check-capabilities.patch
|
||||
|
||||
# adds missing entries to the lockfile
|
||||
# can be removed next update
|
||||
(fetchpatch {
|
||||
name = "fix-lockfile.patch";
|
||||
url = "https://github.com/Mahdi-zarei/nekoray/commit/6f9b2c69e21b0b86242fcc5731f21561373d0963.patch";
|
||||
stripLen = 2;
|
||||
hash = "sha256-LDLgCQUXOqaV++6Z4/8r2IaBM+Kz/LckjVsvZn/0lLM=";
|
||||
})
|
||||
];
|
||||
|
||||
vendorHash = "sha256-6Q6Qi3QQOmuLBaV4t/CEER6s1MUvL7ER6Hfm44sQk4M=";
|
||||
|
||||
# ldflags and tags are taken from script/build_go.sh
|
||||
ldflags = [
|
||||
"-w"
|
||||
"-s"
|
||||
"-X github.com/sagernet/sing-box/constant.Version=${finalAttrs.version}"
|
||||
];
|
||||
|
||||
tags = [
|
||||
"with_clash_api"
|
||||
"with_gvisor"
|
||||
"with_quic"
|
||||
"with_wireguard"
|
||||
"with_utls"
|
||||
"with_ech"
|
||||
"with_dhcp"
|
||||
];
|
||||
};
|
||||
|
||||
# this tricks nix-update into also updating the vendorHash of nekobox-core
|
||||
passthru.goModules = finalAttrs.passthru.nekobox-core.goModules;
|
||||
|
||||
meta = {
|
||||
description = "Qt based cross-platform GUI proxy configuration manager";
|
||||
homepage = "https://github.com/Mahdi-zarei/nekoray";
|
||||
license = lib.licenses.gpl3Plus;
|
||||
mainProgram = "nekoray";
|
||||
maintainers = with lib.maintainers; [
|
||||
tomasajt
|
||||
aleksana
|
||||
];
|
||||
platforms = lib.platforms.linux;
|
||||
};
|
||||
})
|
||||
32
pkgs/by-name/pr/protorpc/package.nix
Normal file
32
pkgs/by-name/pr/protorpc/package.nix
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
lib,
|
||||
buildGoModule,
|
||||
fetchFromGitHub,
|
||||
}:
|
||||
|
||||
buildGoModule (finalAttrs: {
|
||||
pname = "protorpc";
|
||||
version = "1.1.2";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "chai2010";
|
||||
repo = "protorpc";
|
||||
tag = "v${finalAttrs.version}";
|
||||
hash = "sha256-yu4aSWM0TNnGGLAA6NApMekHMi6e+McGRndi8ptdXfY=";
|
||||
};
|
||||
|
||||
vendorHash = "sha256-qGFPUNomcFHRBX33WWdYaAY7196RljwFKuS+EZhKKz8=";
|
||||
|
||||
ldflags = [
|
||||
"-s"
|
||||
"-w"
|
||||
];
|
||||
|
||||
meta = {
|
||||
description = "Google Protocol Protobufs RPC for Go";
|
||||
homepage = "https://github.com/chai2010/protorpc";
|
||||
changelog = "https://github.com/chai2010/protorpc/blob/${finalAttrs.src.rev}/changelog.md";
|
||||
license = lib.licenses.bsd3;
|
||||
maintainers = with lib.maintainers; [ tomasajt ];
|
||||
};
|
||||
})
|
||||
@@ -1,23 +1,22 @@
|
||||
diff --git a/server.go b/server.go
|
||||
index c2a6be0..8aeca1c 100644
|
||||
index e2f2ab3..f81812f 100644
|
||||
--- a/server.go
|
||||
+++ b/server.go
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/service"
|
||||
+ "golang.org/x/sys/unix"
|
||||
"log"
|
||||
"nekobox_core/gen"
|
||||
"nekobox_core/internal/boxbox"
|
||||
@@ -359,13 +360,25 @@ func (s *server) CompileGeoSiteToSrs(ctx context.Context, in *gen.CompileGeoSite
|
||||
"os"
|
||||
"runtime"
|
||||
@@ -349,12 +350,25 @@ func (s *server) CompileGeoSiteToSrs(in *gen.CompileGeoSiteToSrsRequest, out *ge
|
||||
}
|
||||
|
||||
func (s *server) IsPrivileged(ctx context.Context, _ *gen.EmptyReq) (*gen.IsPrivilegedResponse, error) {
|
||||
func (s *server) IsPrivileged(in *gen.EmptyReq, out *gen.IsPrivilegedResponse) error {
|
||||
- if runtime.GOOS == "windows" {
|
||||
- return &gen.IsPrivilegedResponse{
|
||||
- HasPrivilege: false,
|
||||
- }, nil
|
||||
- out.HasPrivilege = To(false)
|
||||
- return nil
|
||||
+ ret := false
|
||||
+ if runtime.GOOS == "windows" || os.Geteuid() == 0 {
|
||||
+ ret = true
|
||||
@@ -36,8 +35,8 @@ index c2a6be0..8aeca1c 100644
|
||||
+ }
|
||||
}
|
||||
|
||||
- return &gen.IsPrivilegedResponse{HasPrivilege: os.Geteuid() == 0}, nil
|
||||
+ return &gen.IsPrivilegedResponse{HasPrivilege: ret}, nil
|
||||
- out.HasPrivilege = To(os.Geteuid() == 0)
|
||||
+ out.HasPrivilege = To(ret)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *server) SpeedTest(ctx context.Context, in *gen.SpeedTestRequest) (*gen.SpeedTestResponse, error) {
|
||||
@@ -1,18 +1,18 @@
|
||||
diff --git a/src/global/NekoGui.cpp b/src/global/NekoGui.cpp
|
||||
diff --git a/src/global/Configs.cpp b/src/global/Configs.cpp
|
||||
index 7943d7a..5bb20cc 100644
|
||||
--- a/src/global/NekoGui.cpp
|
||||
+++ b/src/global/NekoGui.cpp
|
||||
@@ -355,6 +355,12 @@ namespace NekoGui {
|
||||
--- a/src/global/Configs.cpp
|
||||
+++ b/src/global/Configs.cpp
|
||||
@@ -355,6 +355,12 @@ namespace Configs {
|
||||
// System Utils
|
||||
|
||||
QString FindNekoBoxCoreRealPath() {
|
||||
QString FindCoreRealPath() {
|
||||
+ // find in PATH first
|
||||
+ QString path = QStandardPaths::findExecutable("nekobox_core");
|
||||
+ QString path = QStandardPaths::findExecutable("throne-core");
|
||||
+ if (!path.isEmpty()) {
|
||||
+ return path;
|
||||
+ }
|
||||
+
|
||||
auto fn = QApplication::applicationDirPath() + "/nekobox_core";
|
||||
auto fn = QApplication::applicationDirPath() + "/Core";
|
||||
auto fi = QFileInfo(fn);
|
||||
if (fi.isSymLink()) return fi.symLinkTarget();
|
||||
diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp
|
||||
@@ -20,23 +20,23 @@ index 9aa46b2..ba7137a 100644
|
||||
--- a/src/ui/mainwindow.cpp
|
||||
+++ b/src/ui/mainwindow.cpp
|
||||
@@ -125,8 +125,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
NekoGui::dataStore->core_port = MkPort();
|
||||
if (NekoGui::dataStore->core_port <= 0) NekoGui::dataStore->core_port = 19810;
|
||||
Configs::dataStore->core_port = MkPort();
|
||||
if (Configs::dataStore->core_port <= 0) Configs::dataStore->core_port = 19810;
|
||||
|
||||
- auto core_path = QApplication::applicationDirPath() + "/";
|
||||
- core_path += "nekobox_core";
|
||||
+ auto core_path = NekoGui::FindNekoBoxCoreRealPath();
|
||||
- core_path += "Core";
|
||||
+ auto core_path = Configs::FindCoreRealPath();
|
||||
|
||||
QStringList args;
|
||||
args.push_back("nekobox");
|
||||
args.push_back("-port");
|
||||
@@ -844,6 +843,15 @@ bool MainWindow::get_elevated_permissions(int reason) {
|
||||
return true;
|
||||
}
|
||||
if (NekoGui::IsAdmin()) return true;
|
||||
if (Configs::IsAdmin()) return true;
|
||||
+ QMessageBox::critical(
|
||||
+ GetMessageBoxParent(),
|
||||
+ tr("Unable to elevate privileges when installed with Nix"),
|
||||
+ tr("Due to the read-only property of Nix store, we cannot set suid for nekobox_core. If you are using NixOS, please set `programs.nekoray.tunMode.enable` option to elevate privileges."),
|
||||
+ tr("Due to the read-only property of Nix store, we cannot set suid for throne-core. If you are using NixOS, please set `programs.throne.tunMode.enable` option to elevate privileges."),
|
||||
+ QMessageBox::Ok
|
||||
+ );
|
||||
+ return false;
|
||||
153
pkgs/by-name/th/throne/package.nix
Normal file
153
pkgs/by-name/th/throne/package.nix
Normal file
@@ -0,0 +1,153 @@
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
|
||||
buildGoModule,
|
||||
fetchFromGitHub,
|
||||
fetchurl,
|
||||
makeDesktopItem,
|
||||
|
||||
protobuf,
|
||||
protoc-gen-go,
|
||||
protorpc,
|
||||
|
||||
cmake,
|
||||
copyDesktopItems,
|
||||
ninja,
|
||||
|
||||
qt6Packages,
|
||||
|
||||
# override if you want to have more up-to-date rulesets
|
||||
throne-srslist ? fetchurl {
|
||||
url = "https://raw.githubusercontent.com/throneproj/routeprofiles/60eb41122de3aa53c701ec948cd52d7a26adafea/srslist.h";
|
||||
hash = "sha256-k9vPtcusML4GR81UVeJ7jhuDHGk5Qh0eKw/cSOxBd5g=";
|
||||
},
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "throne";
|
||||
version = "1.0.8-unstable-2025-10-29";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "throneproj";
|
||||
repo = "Throne";
|
||||
rev = "54af50fc414ffaf98b3ff88e3dd8aa041c65e041";
|
||||
hash = "sha256-kfvsGw0RUYHkOUSeSA4egLl+gQqN4KkZKXX3CQQzYks=";
|
||||
};
|
||||
|
||||
strictDeps = true;
|
||||
|
||||
nativeBuildInputs = [
|
||||
cmake
|
||||
copyDesktopItems
|
||||
ninja
|
||||
qt6Packages.wrapQtAppsHook
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
qt6Packages.qtbase
|
||||
qt6Packages.qttools
|
||||
];
|
||||
|
||||
cmakeFlags = [
|
||||
# makes sure the app uses the user's config directory to store it's non-static content
|
||||
# it's essentially the same as always setting the -appdata flag when running the program
|
||||
(lib.cmakeBool "NKR_PACKAGE" true)
|
||||
];
|
||||
|
||||
patches = [
|
||||
# disable suid request as it cannot be applied to throne-core in nix store
|
||||
# and prompt users to use NixOS module instead. And use throne-core from PATH
|
||||
# to make use of security wrappers
|
||||
./nixos-disable-setuid-request.patch
|
||||
];
|
||||
|
||||
preBuild = ''
|
||||
ln -s ${throne-srslist} ./srslist.h
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
install -Dm755 Throne -t "$out/share/throne/"
|
||||
install -Dm644 "$src/res/public/Throne.png" -t "$out/share/icons/hicolor/512x512/apps/"
|
||||
|
||||
mkdir -p "$out/bin"
|
||||
ln -s "$out/share/throne/Throne" "$out/bin/"
|
||||
|
||||
ln -s ${finalAttrs.passthru.core}/bin/Core "$out/share/throne/Core"
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
desktopItems = [
|
||||
(makeDesktopItem {
|
||||
name = "throne";
|
||||
desktopName = "Throne";
|
||||
exec = "Throne";
|
||||
icon = "Throne";
|
||||
comment = finalAttrs.meta.description;
|
||||
terminal = false;
|
||||
categories = [ "Network" ];
|
||||
})
|
||||
];
|
||||
|
||||
passthru.core = buildGoModule {
|
||||
pname = "throne-core";
|
||||
inherit (finalAttrs) version src;
|
||||
sourceRoot = "${finalAttrs.src.name}/core/server";
|
||||
|
||||
patches = [
|
||||
# also check cap_net_admin so we don't have to set suid
|
||||
./core-also-check-capabilities.patch
|
||||
];
|
||||
|
||||
proxyVendor = true;
|
||||
vendorHash = "sha256-thMRkbs5fS7KsUSRSeUaB2xkTjs7kJ9AKXW0+OXN3io=";
|
||||
|
||||
nativeBuildInputs = [
|
||||
protobuf
|
||||
protoc-gen-go
|
||||
protorpc
|
||||
];
|
||||
|
||||
# taken from script/build_go.sh
|
||||
preBuild = ''
|
||||
pushd gen
|
||||
protoc -I . --go_out=. --protorpc_out=. libcore.proto
|
||||
popd
|
||||
'';
|
||||
|
||||
# ldflags and tags are taken from script/build_go.sh
|
||||
ldflags = [
|
||||
"-w"
|
||||
"-s"
|
||||
"-X github.com/sagernet/sing-box/constant.Version=${finalAttrs.version}"
|
||||
];
|
||||
|
||||
tags = [
|
||||
"with_clash_api"
|
||||
"with_gvisor"
|
||||
"with_quic"
|
||||
"with_wireguard"
|
||||
"with_utls"
|
||||
"with_dhcp"
|
||||
"with_tailscale"
|
||||
];
|
||||
};
|
||||
|
||||
# this tricks nix-update into also updating the vendorHash of throne-core
|
||||
passthru.goModules = finalAttrs.passthru.core.goModules;
|
||||
|
||||
meta = {
|
||||
description = "Qt based cross-platform GUI proxy configuration manager";
|
||||
homepage = "https://github.com/throneproj/Throne";
|
||||
license = lib.licenses.gpl3Plus;
|
||||
mainProgram = "Throne";
|
||||
maintainers = with lib.maintainers; [
|
||||
tomasajt
|
||||
aleksana
|
||||
];
|
||||
platforms = lib.platforms.linux;
|
||||
};
|
||||
})
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/src/global/NekoGui.cpp b/src/global/NekoGui.cpp
|
||||
diff --git a/src/global/Configs.cpp b/src/global/Configs.cpp
|
||||
index d9be515..15de64b 100644
|
||||
--- a/src/global/NekoGui.cpp
|
||||
+++ b/src/global/NekoGui.cpp
|
||||
@@ -426,8 +426,6 @@ namespace NekoGui {
|
||||
--- a/src/global/Configs.cpp
|
||||
+++ b/src/global/Configs.cpp
|
||||
@@ -426,8 +426,6 @@ namespace Configs {
|
||||
};
|
||||
|
||||
QString GetBasePath() {
|
||||
@@ -1078,6 +1078,7 @@ mapAliases {
|
||||
nats-streaming-server = throw "'nats-streaming-server' has been removed as critical bug fixes and security fixes will no longer be performed as of June of 2023"; # added 2025-10-13
|
||||
ncdu_2 = throw "'ncdu_2' has been renamed to/replaced by 'ncdu'"; # Converted to throw 2025-10-27
|
||||
neardal = throw "neardal has been removed because it has been marked as broken since at least November 2024."; # Added 2025-09-29
|
||||
nekoray = lib.warnOnInstantiate "nekoray has been renamed to throne" throne; # Added 2025-11-10
|
||||
neocities-cli = throw "'neocities-cli' has been renamed to/replaced by 'neocities'"; # Converted to throw 2025-10-27
|
||||
netbox_4_1 = throw "netbox 4.1 series has been removed as it was EOL"; # Added 2025-10-14
|
||||
netbsdCross = throw "'netbsdCross' has been renamed to/replaced by 'netbsd'"; # Converted to throw 2025-10-27
|
||||
|
||||
Reference in New Issue
Block a user