This commit is contained in:
Toast 2025-07-31 17:36:52 +02:00 committed by GitHub
commit 44afafdaf7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 131 additions and 118 deletions

View File

@ -1,10 +1,10 @@
{ {
lib, lib,
stdenv, buildPythonApplication,
makeWrapper,
fetchurl, fetchurl,
util-linux, util-linux,
python, python,
setuptools,
jinja2, jinja2,
impacket, impacket,
pyopenssl, pyopenssl,
@ -15,6 +15,10 @@
pyzmq, pyzmq,
ffmpeg, ffmpeg,
mutagen, mutagen,
pyftpdlib,
magic,
partftpy,
fusepy, # for partyfuse
# use argon2id-hashed passwords in config files (sha2 is always available) # use argon2id-hashed passwords in config files (sha2 is always available)
withHashedPasswords ? true, withHashedPasswords ? true,
@ -40,12 +44,21 @@
# send ZeroMQ messages from event-hooks # send ZeroMQ messages from event-hooks
withZeroMQ ? true, withZeroMQ ? true,
# enable FTP server
withFTP ? true,
# enable FTPS support in the FTP server # enable FTPS support in the FTP server
withFTPS ? false, withFTPS ? false,
# enable TFTP server
withTFTP ? false,
# samba/cifs server; dangerous and buggy, enable if you really need it # samba/cifs server; dangerous and buggy, enable if you really need it
withSMB ? false, withSMB ? false,
# enables filetype detection for nameless uploads
withMagic ? false,
# extra packages to add to the PATH # extra packages to add to the PATH
extraPackages ? [ ], extraPackages ? [ ],
@ -58,14 +71,23 @@
let let
pinData = lib.importJSON ./pin.json; pinData = lib.importJSON ./pin.json;
pyEnv = python.withPackages ( runtimeDeps = ([ util-linux ] ++ extraPackages ++ lib.optional withMediaProcessing ffmpeg);
ps: in
with ps; buildPythonApplication {
pname = "copyparty";
inherit (pinData) version;
src = fetchurl {
inherit (pinData) url hash;
};
dependencies =
[ [
jinja2 jinja2
fusepy
] ]
++ lib.optional withSMB impacket ++ lib.optional withSMB impacket
++ lib.optional withFTP pyftpdlib
++ lib.optional withFTPS pyopenssl ++ lib.optional withFTPS pyopenssl
++ lib.optional withTFTP partftpy
++ lib.optional withCertgen cfssl ++ lib.optional withCertgen cfssl
++ lib.optional withThumbnails pillow ++ lib.optional withThumbnails pillow
++ lib.optional withFastThumbnails pyvips ++ lib.optional withFastThumbnails pyvips
@ -73,25 +95,14 @@ let
++ lib.optional withBasicAudioMetadata mutagen ++ lib.optional withBasicAudioMetadata mutagen
++ lib.optional withHashedPasswords argon2-cffi ++ lib.optional withHashedPasswords argon2-cffi
++ lib.optional withZeroMQ pyzmq ++ lib.optional withZeroMQ pyzmq
++ (extraPythonPackages ps) ++ lib.optional withMagic magic
); ++ (extraPythonPackages python.pkgs);
makeWrapperArgs = [ "--prefix PATH : ${lib.makeBinPath runtimeDeps}" ];
runtimeDeps = ([ util-linux ] ++ extraPackages ++ lib.optional withMediaProcessing ffmpeg); pyproject = true;
in build-system = [
stdenv.mkDerivation { setuptools
pname = "copyparty"; ];
inherit (pinData) version;
src = fetchurl {
inherit (pinData) url hash;
};
nativeBuildInputs = [ makeWrapper ];
dontUnpack = true;
installPhase = ''
install -Dm755 $src $out/share/copyparty-sfx.py
makeWrapper ${pyEnv.interpreter} $out/bin/copyparty \
--prefix PATH : ${lib.makeBinPath runtimeDeps} \
--add-flag $out/share/copyparty-sfx.py
'';
meta = { meta = {
description = "Turn almost any device into a file server"; description = "Turn almost any device into a file server";
longDescription = '' longDescription = ''
@ -101,8 +112,7 @@ stdenv.mkDerivation {
homepage = "https://github.com/9001/copyparty"; homepage = "https://github.com/9001/copyparty";
changelog = "https://github.com/9001/copyparty/releases/tag/v${pinData.version}"; changelog = "https://github.com/9001/copyparty/releases/tag/v${pinData.version}";
license = lib.licenses.mit; license = lib.licenses.mit;
inherit (python.meta) platforms;
mainProgram = "copyparty"; mainProgram = "copyparty";
sourceProvenance = [ lib.sourceTypes.binaryBytecode ]; sourceProvenance = [ lib.sourceTypes.fromSource ];
}; };
} }

View File

@ -1,5 +1,5 @@
{ {
"url": "https://github.com/9001/copyparty/releases/download/v1.18.8/copyparty-sfx.py", "url": "https://github.com/9001/copyparty/releases/download/v1.18.8/copyparty-1.18.8.tar.gz",
"version": "1.18.8", "version": "1.18.8",
"hash": "sha256-ZQtpGVcC6fmJCC65hXN4v3ERiB3jPz+oqvFHtbxR8+o=" "hash": "sha256-2K8BL1Uqbx38UB0M6UmQuTLUIt909q+2bKKjGYX1gJs="
} }

View File

@ -2,23 +2,18 @@
# Update the Nix package pin # Update the Nix package pin
# #
# Usage: ./update.sh [PATH] # Usage: ./update.sh
# When the [PATH] is not set, it will fetch the latest release from the repo.
# With [PATH] set, it will hash the given file and generate the URL,
# base on the version contained within the file
import base64 import base64
import json import json
import hashlib import hashlib
import sys import sys
import re
from pathlib import Path from pathlib import Path
OUTPUT_FILE = Path("pin.json") OUTPUT_FILE = Path("pin.json")
TARGET_ASSET = "copyparty-sfx.py" TARGET_ASSET = lambda version: f"copyparty-{version}.tar.gz"
HASH_TYPE = "sha256" HASH_TYPE = "sha256"
LATEST_RELEASE_URL = "https://api.github.com/repos/9001/copyparty/releases/latest" LATEST_RELEASE_URL = "https://api.github.com/repos/9001/copyparty/releases/latest"
DOWNLOAD_URL = lambda version: f"https://github.com/9001/copyparty/releases/download/v{version}/{TARGET_ASSET}"
def get_formatted_hash(binary): def get_formatted_hash(binary):
@ -29,20 +24,12 @@ def get_formatted_hash(binary):
return f"{HASH_TYPE}-{encoded_hash}" return f"{HASH_TYPE}-{encoded_hash}"
def version_from_sfx(binary):
result = re.search(b'^VER = "(.*)"$', binary, re.MULTILINE)
if result:
return result.groups(1)[0].decode("ascii")
raise ValueError("version not found in provided file")
def remote_release_pin(): def remote_release_pin():
import requests import requests
response = requests.get(LATEST_RELEASE_URL).json() response = requests.get(LATEST_RELEASE_URL).json()
version = response["tag_name"].lstrip("v") version = response["tag_name"].lstrip("v")
asset_info = [a for a in response["assets"] if a["name"] == TARGET_ASSET][0] asset_info = [a for a in response["assets"] if a["name"] == TARGET_ASSET(version)][0]
download_url = asset_info["browser_download_url"] download_url = asset_info["browser_download_url"]
asset = requests.get(download_url) asset = requests.get(download_url)
formatted_hash = get_formatted_hash(asset.content) formatted_hash = get_formatted_hash(asset.content)
@ -51,22 +38,8 @@ def remote_release_pin():
return result return result
def local_release_pin(path):
asset = path.read_bytes()
version = version_from_sfx(asset)
download_url = DOWNLOAD_URL(version)
formatted_hash = get_formatted_hash(asset)
result = {"url": download_url, "version": version, "hash": formatted_hash}
return result
def main(): def main():
if len(sys.argv) > 1: result = remote_release_pin()
asset_path = Path(sys.argv[1])
result = local_release_pin(asset_path)
else:
result = remote_release_pin()
print(result) print(result)
json_result = json.dumps(result, indent=4) json_result = json.dumps(result, indent=4)

View File

@ -0,0 +1,30 @@
{
lib,
buildPythonPackage,
fetchurl,
setuptools,
}:
let
pinData = lib.importJSON ./pin.json;
in
buildPythonPackage rec {
pname = "partftpy";
inherit (pinData) version;
pyproject = true;
src = fetchurl {
inherit (pinData) url hash;
};
build-system = [ setuptools ];
pythonImportsCheck = [ "partftpy.TftpServer" ];
meta = {
description = "Pure Python TFTP library (copyparty edition)";
homepage = "https://github.com/9001/partftpy";
changelog = "https://github.com/9001/partftpy/releases/tag/${version}";
license = lib.licenses.mit;
};
}

View File

@ -0,0 +1,5 @@
{
"url": "https://github.com/9001/partftpy/releases/download/v0.4.0/partftpy-0.4.0.tar.gz",
"version": "0.4.0",
"hash": "sha256-5Q2zyuJ892PGZmb+YXg0ZPW/DK8RDL1uE0j5HPd4We0="
}

View File

@ -0,0 +1,50 @@
#!/usr/bin/env python3
# Update the Nix package pin
#
# Usage: ./update.sh
import base64
import json
import hashlib
import sys
from pathlib import Path
OUTPUT_FILE = Path("pin.json")
TARGET_ASSET = lambda version: f"partftpy-{version}.tar.gz"
HASH_TYPE = "sha256"
LATEST_RELEASE_URL = "https://api.github.com/repos/9001/partftpy/releases/latest"
def get_formatted_hash(binary):
hasher = hashlib.new("sha256")
hasher.update(binary)
asset_hash = hasher.digest()
encoded_hash = base64.b64encode(asset_hash).decode("ascii")
return f"{HASH_TYPE}-{encoded_hash}"
def remote_release_pin():
import requests
response = requests.get(LATEST_RELEASE_URL).json()
version = response["tag_name"].lstrip("v")
asset_info = [a for a in response["assets"] if a["name"] == TARGET_ASSET(version)][0]
download_url = asset_info["browser_download_url"]
asset = requests.get(download_url)
formatted_hash = get_formatted_hash(asset.content)
result = {"url": download_url, "version": version, "hash": formatted_hash}
return result
def main():
result = remote_release_pin()
print(result)
json_result = json.dumps(result, indent=4)
OUTPUT_FILE.write_text(json_result)
if __name__ == "__main__":
main()

View File

@ -1,26 +0,0 @@
{
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
'';
}

View File

@ -1,24 +0,0 @@
{
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
'';
}

View File

@ -12,17 +12,14 @@
}: }:
{ {
nixosModules.default = ./contrib/nixos/modules/copyparty.nix; nixosModules.default = ./contrib/nixos/modules/copyparty.nix;
overlays.default = final: prev: rec { overlays.default = final: prev: {
copyparty = final.python3.pkgs.callPackage ./contrib/package/nix/copyparty { copyparty = final.python3.pkgs.callPackage ./contrib/package/nix/copyparty {
ffmpeg = final.ffmpeg-full; ffmpeg = final.ffmpeg-full;
}; };
python3 = prev.python3.override {
partyfuse = prev.callPackage ./contrib/package/nix/partyfuse { packageOverrides = pyFinal: pyPrev: {
inherit copyparty; partftpy = pyFinal.callPackage ./contrib/package/nix/partftpy { };
}; };
u2c = prev.callPackage ./contrib/package/nix/u2c {
inherit copyparty;
}; };
}; };
} }
@ -54,8 +51,6 @@
packages = { packages = {
inherit (pkgs) inherit (pkgs)
copyparty copyparty
partyfuse
u2c
; ;
default = self.packages.${system}.copyparty; default = self.packages.${system}.copyparty;
}; };