mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
various improvements to the nix files (#228)
* nix: allow passing extra packages in PATH * nix: allow passing extra python packages I wanted to use https://github.com/9001/copyparty/blob/hovudstraum/bin/hooks/notify.py but that wasn't really possible without this under the nix package. * nix: format all nix files with nixfmt * nix: reduce redundancy in the package For readability * nix: remove unused pyftpdlib import * nix: put makeWrapper into the correct inputs * nix: fill out all of meta * nix: set formatter in flake for nix files This allows contributors to format their nix changes with the `nix fmt` command. * nix: add u2c * nix: add partyfuse One downside of the way the nix ecosystem works is that MacFUSE needs to be installed manually. Luckily the script tells you that already! * nix: add missing cfssl import * nix: add flake check that makes sure it builds with all flags Because sometimes an import might be missing, and if it is an optional then you'll only figure out that it's broken if you set the flag. * nix: use correct overlay argument names Or `nix flake check` will refuse to run the copyparty-full check
This commit is contained in:
parent
735d9f9391
commit
4915b14be1
|
@ -4,26 +4,29 @@
|
|||
lib,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
mkKeyValue = key: value:
|
||||
if value == true
|
||||
then
|
||||
with lib;
|
||||
let
|
||||
mkKeyValue =
|
||||
key: value:
|
||||
if value == true then
|
||||
# sets with a true boolean value are coerced to just the key name
|
||||
key
|
||||
else if value == false
|
||||
then
|
||||
else if value == false then
|
||||
# or omitted completely when false
|
||||
""
|
||||
else (generators.mkKeyValueDefault {inherit mkValueString;} ": " key value);
|
||||
else
|
||||
(generators.mkKeyValueDefault { inherit mkValueString; } ": " key value);
|
||||
|
||||
mkAttrsString = value: (generators.toKeyValue { inherit mkKeyValue; } value);
|
||||
|
||||
mkValueString = value:
|
||||
if isList value
|
||||
then (concatStringsSep ", " (map mkValueString value))
|
||||
else if isAttrs value
|
||||
then "\n" + (mkAttrsString value)
|
||||
else (generators.mkValueStringDefault {} value);
|
||||
mkValueString =
|
||||
value:
|
||||
if isList value then
|
||||
(concatStringsSep ", " (map mkValueString value))
|
||||
else if isAttrs value then
|
||||
"\n" + (mkAttrsString value)
|
||||
else
|
||||
(generators.mkValueStringDefault { } value);
|
||||
|
||||
mkSectionName = value: "[" + (escape [ "[" "]" ] value) + "]";
|
||||
|
||||
|
@ -57,7 +60,8 @@ with lib; let
|
|||
externalCacheDir = "/var/cache/copyparty";
|
||||
externalStateDir = "/var/lib/copyparty";
|
||||
defaultShareDir = "${externalStateDir}/data";
|
||||
in {
|
||||
in
|
||||
{
|
||||
options.services.copyparty = {
|
||||
enable = mkEnableOption "web-based file manager";
|
||||
|
||||
|
@ -128,7 +132,10 @@ in {
|
|||
};
|
||||
|
||||
accounts = mkOption {
|
||||
type = types.attrsOf (types.submodule ({...}: {
|
||||
type = types.attrsOf (
|
||||
types.submodule (
|
||||
{ ... }:
|
||||
{
|
||||
options = {
|
||||
passwordFile = mkOption {
|
||||
type = types.str;
|
||||
|
@ -139,7 +146,9 @@ in {
|
|||
example = "/run/keys/copyparty/ed";
|
||||
};
|
||||
};
|
||||
}));
|
||||
}
|
||||
)
|
||||
);
|
||||
description = ''
|
||||
A set of copyparty accounts to create.
|
||||
'';
|
||||
|
@ -152,7 +161,10 @@ in {
|
|||
};
|
||||
|
||||
volumes = mkOption {
|
||||
type = types.attrsOf (types.submodule ({...}: {
|
||||
type = types.attrsOf (
|
||||
types.submodule (
|
||||
{ ... }:
|
||||
{
|
||||
options = {
|
||||
path = mkOption {
|
||||
type = types.path;
|
||||
|
@ -214,12 +226,16 @@ in {
|
|||
default = { };
|
||||
};
|
||||
};
|
||||
}));
|
||||
}
|
||||
)
|
||||
);
|
||||
description = "A set of copyparty volumes to create";
|
||||
default = {
|
||||
"/" = {
|
||||
path = defaultShareDir;
|
||||
access = {r = "*";};
|
||||
access = {
|
||||
r = "*";
|
||||
};
|
||||
};
|
||||
};
|
||||
example = literalExpression ''
|
||||
|
@ -238,9 +254,11 @@ in {
|
|||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable (let
|
||||
config = mkIf cfg.enable (
|
||||
let
|
||||
command = "${getExe cfg.package} -c ${runtimeConfigPath}";
|
||||
in {
|
||||
in
|
||||
{
|
||||
systemd.services.copyparty = {
|
||||
description = "http file sharing hub";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
@ -250,15 +268,16 @@ in {
|
|||
XDG_CONFIG_HOME = externalStateDir;
|
||||
};
|
||||
|
||||
preStart = let
|
||||
replaceSecretCommand = name: attrs: "${getExe pkgs.replace-secret} '${
|
||||
passwordPlaceholder name
|
||||
}' '${attrs.passwordFile}' ${runtimeConfigPath}";
|
||||
in ''
|
||||
preStart =
|
||||
let
|
||||
replaceSecretCommand =
|
||||
name: attrs:
|
||||
"${getExe pkgs.replace-secret} '${passwordPlaceholder name}' '${attrs.passwordFile}' ${runtimeConfigPath}";
|
||||
in
|
||||
''
|
||||
set -euo pipefail
|
||||
install -m 600 ${configFile} ${runtimeConfigPath}
|
||||
${concatStringsSep "\n"
|
||||
(mapAttrsToList replaceSecretCommand cfg.accounts)}
|
||||
${concatStringsSep "\n" (mapAttrsToList replaceSecretCommand cfg.accounts)}
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
|
@ -274,22 +293,16 @@ in {
|
|||
CacheDirectory = lib.mkIf (cfg.settings ? hist) [ "copyparty" ];
|
||||
CacheDirectoryMode = lib.mkIf (cfg.settings ? hist) "0700";
|
||||
WorkingDirectory = externalStateDir;
|
||||
BindReadOnlyPaths =
|
||||
[
|
||||
BindReadOnlyPaths = [
|
||||
"/nix/store"
|
||||
"-/etc/resolv.conf"
|
||||
"-/etc/nsswitch.conf"
|
||||
"-/etc/group"
|
||||
"-/etc/hosts"
|
||||
"-/etc/localtime"
|
||||
]
|
||||
++ (mapAttrsToList (k: v: "-${v.passwordFile}") cfg.accounts);
|
||||
] ++ (mapAttrsToList (k: v: "-${v.passwordFile}") cfg.accounts);
|
||||
BindPaths =
|
||||
(
|
||||
if cfg.settings ? hist
|
||||
then [cfg.settings.hist]
|
||||
else []
|
||||
)
|
||||
(if cfg.settings ? hist then [ cfg.settings.hist ] else [ ])
|
||||
++ [ externalStateDir ]
|
||||
++ (mapAttrsToList (k: v: v.path) cfg.volumes);
|
||||
# ProtectSystem = "strict";
|
||||
|
@ -333,8 +346,7 @@ in {
|
|||
mode = ":755";
|
||||
};
|
||||
}
|
||||
)
|
||||
cfg.volumes
|
||||
) cfg.volumes
|
||||
);
|
||||
|
||||
users.groups.copyparty = lib.mkIf (cfg.user == "copyparty" && cfg.group == "copyparty") { };
|
||||
|
@ -345,9 +357,7 @@ in {
|
|||
isSystemUser = true;
|
||||
};
|
||||
environment.systemPackages = lib.mkIf cfg.mkHashWrapper [
|
||||
(pkgs.writeShellScriptBin
|
||||
"copyparty-hash"
|
||||
''
|
||||
(pkgs.writeShellScriptBin "copyparty-hash" ''
|
||||
set -a # automatically export variables
|
||||
# set same environment variables as the systemd service
|
||||
${lib.pipe config.systemd.services.copyparty.environment [
|
||||
|
@ -360,5 +370,6 @@ in {
|
|||
exec ${command} --ah-cli
|
||||
'')
|
||||
];
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,20 @@
|
|||
{ lib, stdenv, makeWrapper, fetchurl, util-linux, python, jinja2, impacket, pyftpdlib, pyopenssl, argon2-cffi, pillow, pyvips, pyzmq, ffmpeg, mutagen,
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
makeWrapper,
|
||||
fetchurl,
|
||||
util-linux,
|
||||
python,
|
||||
jinja2,
|
||||
impacket,
|
||||
pyopenssl,
|
||||
cfssl,
|
||||
argon2-cffi,
|
||||
pillow,
|
||||
pyvips,
|
||||
pyzmq,
|
||||
ffmpeg,
|
||||
mutagen,
|
||||
|
||||
# use argon2id-hashed passwords in config files (sha2 is always available)
|
||||
withHashedPasswords ? true,
|
||||
|
@ -30,12 +46,22 @@ withFTPS ? false,
|
|||
# samba/cifs server; dangerous and buggy, enable if you really need it
|
||||
withSMB ? false,
|
||||
|
||||
# extra packages to add to the PATH
|
||||
extraPackages ? [ ],
|
||||
|
||||
# function that accepts a python packageset and returns a list of packages to
|
||||
# be added to the python venv. useful for scripts and such that require
|
||||
# additional dependencies
|
||||
extraPythonPackages ? (_p: [ ]),
|
||||
|
||||
}:
|
||||
|
||||
let
|
||||
pinData = lib.importJSON ./pin.json;
|
||||
pyEnv = python.withPackages (ps:
|
||||
with ps; [
|
||||
pyEnv = python.withPackages (
|
||||
ps:
|
||||
with ps;
|
||||
[
|
||||
jinja2
|
||||
]
|
||||
++ lib.optional withSMB impacket
|
||||
|
@ -47,22 +73,36 @@ let
|
|||
++ lib.optional withBasicAudioMetadata mutagen
|
||||
++ lib.optional withHashedPasswords argon2-cffi
|
||||
++ lib.optional withZeroMQ pyzmq
|
||||
++ (extraPythonPackages ps)
|
||||
);
|
||||
in stdenv.mkDerivation {
|
||||
|
||||
runtimeDeps = ([ util-linux ] ++ extraPackages ++ lib.optional withMediaProcessing ffmpeg);
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
pname = "copyparty";
|
||||
version = pinData.version;
|
||||
inherit (pinData) version;
|
||||
src = fetchurl {
|
||||
url = pinData.url;
|
||||
hash = pinData.hash;
|
||||
inherit (pinData) url hash;
|
||||
};
|
||||
buildInputs = [ makeWrapper ];
|
||||
nativeBuildInputs = [ makeWrapper ];
|
||||
dontUnpack = true;
|
||||
dontBuild = true;
|
||||
installPhase = ''
|
||||
install -Dm755 $src $out/share/copyparty-sfx.py
|
||||
makeWrapper ${pyEnv.interpreter} $out/bin/copyparty \
|
||||
--set PATH '${lib.makeBinPath ([ util-linux ] ++ lib.optional withMediaProcessing ffmpeg)}:$PATH' \
|
||||
--add-flags "$out/share/copyparty-sfx.py"
|
||||
--prefix PATH : ${lib.makeBinPath runtimeDeps} \
|
||||
--add-flag $out/share/copyparty-sfx.py
|
||||
'';
|
||||
meta.mainProgram = "copyparty";
|
||||
meta = {
|
||||
description = "Turn almost any device into a file server";
|
||||
longDescription = ''
|
||||
Portable file server with accelerated resumable uploads, dedup, WebDAV,
|
||||
FTP, TFTP, zeroconf, media indexer, thumbnails++ all in one file, no deps
|
||||
'';
|
||||
homepage = "https://github.com/9001/copyparty";
|
||||
changelog = "https://github.com/9001/copyparty/releases/tag/v${pinData.version}";
|
||||
license = lib.licenses.mit;
|
||||
inherit (python.meta) platforms;
|
||||
mainProgram = "copyparty";
|
||||
sourceProvenance = [ lib.sourceTypes.binaryBytecode ];
|
||||
};
|
||||
}
|
||||
|
|
26
contrib/package/nix/partyfuse/default.nix
Normal file
26
contrib/package/nix/partyfuse/default.nix
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
stdenvNoCC,
|
||||
copyparty,
|
||||
python3,
|
||||
makeBinaryWrapper,
|
||||
}:
|
||||
let
|
||||
python = python3.withPackages (p: [ p.fusepy ]);
|
||||
in
|
||||
stdenvNoCC.mkDerivation {
|
||||
pname = "partyfuse";
|
||||
inherit (copyparty) version meta;
|
||||
src = ../../../..;
|
||||
|
||||
nativeBuildInputs = [ makeBinaryWrapper ];
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
install -Dm444 bin/partyfuse.py -t $out/share/copyparty
|
||||
makeWrapper ${python.interpreter} $out/bin/partyfuse \
|
||||
--add-flag $out/share/copyparty/partyfuse.py
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
}
|
24
contrib/package/nix/u2c/default.nix
Normal file
24
contrib/package/nix/u2c/default.nix
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
stdenvNoCC,
|
||||
copyparty,
|
||||
python312,
|
||||
makeBinaryWrapper,
|
||||
}:
|
||||
stdenvNoCC.mkDerivation {
|
||||
pname = "u2c";
|
||||
inherit (copyparty) version meta;
|
||||
src = ../../../..;
|
||||
|
||||
nativeBuildInputs = [ makeBinaryWrapper ];
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
install -Dm444 bin/u2c.py -t $out/share/copyparty
|
||||
mkdir $out/bin
|
||||
makeWrapper ${python312.interpreter} $out/bin/u2c \
|
||||
--add-flag $out/share/copyparty/u2c.py
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
}
|
53
flake.nix
53
flake.nix
|
@ -4,16 +4,30 @@
|
|||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
}:
|
||||
{
|
||||
nixosModules.default = ./contrib/nixos/modules/copyparty.nix;
|
||||
overlays.default = self: super: {
|
||||
copyparty =
|
||||
self.python3.pkgs.callPackage ./contrib/package/nix/copyparty {
|
||||
ffmpeg = self.ffmpeg-full;
|
||||
overlays.default = final: prev: rec {
|
||||
copyparty = final.python3.pkgs.callPackage ./contrib/package/nix/copyparty {
|
||||
ffmpeg = final.ffmpeg-full;
|
||||
};
|
||||
|
||||
partyfuse = prev.callPackage ./contrib/package/nix/partyfuse {
|
||||
inherit copyparty;
|
||||
};
|
||||
|
||||
u2c = prev.callPackage ./contrib/package/nix/u2c {
|
||||
inherit copyparty;
|
||||
};
|
||||
};
|
||||
} // flake-utils.lib.eachDefaultSystem (system:
|
||||
}
|
||||
// flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
|
@ -22,10 +36,31 @@
|
|||
};
|
||||
overlays = [ self.overlays.default ];
|
||||
};
|
||||
in {
|
||||
in
|
||||
{
|
||||
# check that copyparty builds with all optionals turned on
|
||||
checks.copyparty-full = self.packages.${system}.copyparty.override {
|
||||
withHashedPasswords = true;
|
||||
withCertgen = true;
|
||||
withThumbnails = true;
|
||||
withFastThumbnails = true;
|
||||
withMediaProcessing = true;
|
||||
withBasicAudioMetadata = true;
|
||||
withZeroMQ = true;
|
||||
withFTPS = true;
|
||||
withSMB = true;
|
||||
};
|
||||
|
||||
packages = {
|
||||
inherit (pkgs) copyparty;
|
||||
inherit (pkgs)
|
||||
copyparty
|
||||
partyfuse
|
||||
u2c
|
||||
;
|
||||
default = self.packages.${system}.copyparty;
|
||||
};
|
||||
});
|
||||
|
||||
formatter = pkgs.nixfmt-tree;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue