shelfmark: init at 1.2.0, add NixOS module and test (#498942)

This commit is contained in:
dotlambda
2026-03-31 03:42:58 +00:00
committed by GitHub
6 changed files with 274 additions and 0 deletions
+1
View File
@@ -957,6 +957,7 @@
./services/misc/servarr/sonarr.nix
./services/misc/servarr/whisparr.nix
./services/misc/serviio.nix
./services/misc/shelfmark.nix
./services/misc/shoko.nix
./services/misc/sickbeard.nix
./services/misc/snapper.nix
+131
View File
@@ -0,0 +1,131 @@
{
lib,
pkgs,
config,
...
}:
let
cfg = config.services.shelfmark;
in
{
options.services.shelfmark = {
enable = lib.mkEnableOption "Shelfmark, a self-hosted book and audiobook search and download interface";
package = lib.mkPackageOption pkgs "shelfmark" { };
openFirewall = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Open the appropriate ports in the firewall for Shelfmark.";
};
environment = lib.mkOption {
type = lib.types.submodule {
freeformType = lib.types.attrsOf lib.types.str;
options = {
FLASK_HOST = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1";
example = "0.0.0.0";
description = "The IP address to bind the Shelfmark server to.";
};
FLASK_PORT = lib.mkOption {
type = lib.types.port;
default = 8084;
apply = toString;
description = "TCP port for the Shelfmark web interface.";
};
ENABLE_LOGGING = lib.mkOption {
type = lib.types.bool;
apply = toString;
default = false;
description = "Whether to enable file logging. Disabled by default since systemd captures console output via journald.";
};
CONFIG_DIR = lib.mkOption {
type = lib.types.path;
default = "/var/lib/shelfmark";
description = "Directory for Shelfmark configuration, database, and artwork cache.";
};
};
};
default = { };
example = {
SEARCH_MODE = "universal";
LOG_LEVEL = "DEBUG";
};
description = ''
Environment variables to pass to the Shelfmark service.
See <https://github.com/calibrain/shelfmark/blob/main/docs/environment-variables.md>
for available options.
'';
};
};
config = lib.mkIf cfg.enable {
systemd.services.shelfmark = {
description = "Shelfmark - book and audiobook search and download interface";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
inherit (cfg) environment;
serviceConfig = {
DynamicUser = true;
ExecStart = "${lib.getExe cfg.package} -b ${cfg.environment.FLASK_HOST}:${cfg.environment.FLASK_PORT}";
StateDirectory = "shelfmark";
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
PrivateDevices = true;
PrivateMounts = true;
ProtectControlGroups = true;
ProtectKernelTunables = true;
RestrictSUIDSGID = true;
RemoveIPC = true;
UMask = "0077";
CapabilityBoundingSet = [ "" ];
NoNewPrivileges = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectClock = true;
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
"~@privileged"
"~@resources"
];
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
PrivateUsers = true;
LockPersonality = true;
ProtectHostname = true;
RestrictRealtime = true;
RestrictNamespaces = true;
ProtectProc = "invisible";
ProcSubset = "pid";
DeviceAllow = [ "" ];
};
};
networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts = [ (lib.toInt cfg.environment.FLASK_PORT) ];
};
};
meta = {
maintainers = with lib.maintainers; [ jamiemagee ];
};
}
+1
View File
@@ -1461,6 +1461,7 @@ in
shadps4 = runTest ./shadps4.nix;
sharkey = runTest ./web-apps/sharkey.nix;
shattered-pixel-dungeon = runTest ./shattered-pixel-dungeon.nix;
shelfmark = runTest ./shelfmark.nix;
shiori = runTest ./shiori.nix;
shoko = import ./shoko.nix { inherit runTest; };
signal-desktop = runTest ./signal-desktop.nix;
+20
View File
@@ -0,0 +1,20 @@
{ lib, ... }:
{
name = "shelfmark";
meta.maintainers = with lib.maintainers; [ jamiemagee ];
nodes.machine = {
services.shelfmark = {
enable = true;
environment.FLASK_HOST = "0.0.0.0";
};
};
testScript = ''
machine.wait_for_unit("shelfmark.service")
machine.wait_for_open_port(8084)
with subtest("Health endpoint responds"):
machine.succeed("curl --fail http://localhost:8084/api/health")
'';
}
@@ -0,0 +1,25 @@
{
buildNpmPackage,
shelfmark,
}:
buildNpmPackage (finalAttrs: {
pname = "shelfmark-frontend";
inherit (shelfmark) version src;
sourceRoot = "${finalAttrs.src.name}/src/frontend";
npmDepsHash = "sha256-RAzotFGj0FGpfF7iyB5f2fdKFvMLcpJx142yplRwboU=";
installPhase = ''
runHook preInstall
cp -r dist $out
runHook postInstall
'';
meta = {
description = "Shelfmark frontend";
homepage = "https://github.com/calibrain/shelfmark/tree/main/src/frontend";
inherit (shelfmark.meta) changelog license maintainers;
};
})
+96
View File
@@ -0,0 +1,96 @@
{
lib,
stdenv,
fetchFromGitHub,
python3Packages,
makeWrapper,
nixosTests,
shelfmark-frontend,
unrar-free,
}:
let
pythonDeps = with python3Packages; [
flask
flask-cors
flask-socketio
python-socketio
requests
pysocks
defusedxml
beautifulsoup4
tqdm
dnspython
gunicorn
gevent
gevent-websocket
psutil
emoji
rarfile
qbittorrent-api
transmission-rpc
authlib
apprise
];
in
stdenv.mkDerivation (finalAttrs: {
pname = "shelfmark";
version = "1.2.0";
src = fetchFromGitHub {
owner = "calibrain";
repo = "shelfmark";
tag = "v${finalAttrs.version}";
hash = "sha256-t4t7je7Y/aezx/EX7paJIcsCq5qyZeU/+mPLeZ8oTPg=";
};
nativeBuildInputs = [
python3Packages.wrapPython
makeWrapper
];
pythonPath = pythonDeps;
installPhase = ''
runHook preInstall
wrapPythonPrograms
mkdir -p $out/libexec $out/bin
cp -r shelfmark $out/libexec/shelfmark
cp -r data $out/libexec/data
ln -s ${finalAttrs.passthru.frontend} $out/libexec/frontend-dist
makeWrapper ${python3Packages.python.interpreter} $out/bin/shelfmark \
--prefix PATH : ${
lib.makeBinPath [
python3Packages.python
unrar-free
]
} \
--set PYTHONPATH "$out/libexec:$program_PYTHONPATH" \
--set USING_EXTERNAL_BYPASSER true \
--add-flags "-m gunicorn.app.wsgiapp --worker-class geventwebsocket.gunicorn.workers.GeventWebSocketWorker --workers 1 -t 300 shelfmark.main:app"
runHook postInstall
'';
passthru = {
frontend = shelfmark-frontend.override {
shelfmark = finalAttrs.finalPackage;
};
tests = {
inherit (nixosTests) shelfmark;
};
};
meta = {
description = "Self-hosted web interface for searching and downloading books and audiobooks";
homepage = "https://github.com/calibrain/shelfmark";
changelog = "https://github.com/calibrain/shelfmark/releases/tag/${finalAttrs.src.tag}";
license = lib.licenses.mit;
maintainers = [ lib.maintainers.jamiemagee ];
mainProgram = "shelfmark";
};
})