Inventree: init pkgs, modules (#478011)

This commit is contained in:
Janne Heß 2026-03-11 13:21:14 +00:00 committed by GitHub
commit b3faef430c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 1669 additions and 0 deletions

View file

@ -14589,6 +14589,12 @@
githubId = 2943605;
name = "Evgeny Kurnevsky";
};
kurogeek = {
email = "kurogeek@lmvhaus.com";
github = "kurogeek";
githubId = 7752934;
name = "kurogeek";
};
kuwii = {
name = "kuwii";
email = "kuwii.someone@gmail.com";

View file

@ -878,6 +878,7 @@
./services/misc/ihaskell.nix
./services/misc/iio-niri.nix
./services/misc/input-remapper.nix
./services/misc/inventree.nix
./services/misc/invidious-router.nix
./services/misc/irkerd.nix
./services/misc/jackett.nix

View file

@ -0,0 +1,446 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.services.inventree;
pkg = cfg.package;
mysqlLocal = cfg.database.createLocally && cfg.database.dbtype == "mysql";
pgsqlLocal = cfg.database.createLocally && cfg.database.dbtype == "postgresql";
manage = pkgs.writeShellScriptBin "inventree-manage" ''
set -a
${lib.toShellVars cfg.settings}
${lib.optionalString (
cfg.database.passwordFile != null
) ''INVENTREE_DB_PASSWORD="$(<"${cfg.database.passwordFile}")"''}
set +a
pushd "${cfg.dataDir}"
sudo=exec
if [[ "$USER" != ${cfg.user} ]]; then
${
if config.security.sudo.enable then
"sudo='exec ${config.security.wrapperDir}/sudo -u ${cfg.user} -E'"
else
">&2 echo 'Aborting, inventree-manage must be run as user `${cfg.user}`!'; exit 2"
}
fi
$sudo ${cfg.package}/bin/inventree "$@"
popd
'';
in
{
meta.buildDocsInSandbox = false;
meta.maintainers = with lib.maintainers; [
kurogeek
];
options.services.inventree = with lib; {
enable = lib.mkEnableOption "inventree";
dataDir = mkOption {
type = types.str;
default = "/var/lib/inventree";
description = "Inventree's data storage path. Will be `/var/lib/inventree` by default.";
};
package = mkOption {
type = types.package;
description = "Which package to use for the InvenTree instance.";
default = pkgs.inventree;
defaultText = literalExpression "pkgs.inventree";
};
adminPasswordFile = mkOption {
type = types.nullOr lib.types.path;
default = null;
example = "/run/keys/inventree-password";
description = "Path to a file containing admin password";
};
secretKeyFile = mkOption {
type = types.path;
default = "${cfg.dataDir}/secret_key.txt";
defaultText = lib.literalExpression ''"''${cfg.dataDir}/secret_key.txt"'';
example = "/run/keys/inventree-secret-key";
description = ''
Path to a file containing the secret key
'';
};
database = {
dbtype = mkOption {
type = lib.types.nullOr (
lib.types.enum [
"postgresql"
"mysql"
]
);
default = "postgresql";
description = "Database type.";
};
dbhost = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default =
if pgsqlLocal then
"/run/postgresql"
else if mysqlLocal then
"/run/mysqld/mysqld.sock"
else
"localhost";
defaultText = "localhost";
example = "localhost";
description = ''
Database host or socket path.
If [](#opt-services.inventree.database.createLocally) is true and
[](#opt-services.inventree.database.dbtype) is either `postgresql` or `mysql`,
defaults to the correct Unix socket instead.
'';
};
dbport = mkOption {
type = types.port;
default = 5432;
description = "Database host port.";
};
dbname = mkOption {
type = types.str;
default = "inventree";
description = "Database name.";
};
dbuser = mkOption {
type = types.str;
default = "inventree";
description = "Database username.";
};
passwordFile = mkOption {
type = with types; nullOr path;
default = null;
example = "/run/keys/inventree-dbpassword";
description = ''
A file containing the password corresponding to
<option>database.dbuser</option>.
'';
};
createLocally = mkOption {
type = types.bool;
default = true;
description = "Create the database and database user locally.";
};
};
domain = mkOption {
type = types.str;
default = "localhost";
example = "inventree.example.com";
description = ''
The INVENTREE_SITE_URL option defines the base URL for the
InvenTree server. This is a critical setting, and it is required
for correct operation of the server. If not specified, the
server will attempt to determine the site URL automatically -
but this may not always be correct!
The site URL is the URL that users will use to access the
InvenTree server. For example, if the server is accessible at
`https://inventree.example.com`, the site URL should be set to
`https://inventree.example.com`. Note that this is not
necessarily the same as the internal URL that the server is
running on - the internal URL will depend entirely on your
server configuration and may be obscured by a reverse proxy or
other such setup.
'';
};
user = mkOption {
type = types.str;
default = "inventree";
description = "User under which InvenTree runs.";
};
group = mkOption {
type = types.str;
default = "inventree";
description = "Group under which InvenTree runs.";
};
settings = mkOption {
type =
with lib.types;
attrsOf (
nullOr (oneOf [
path
str
])
);
default = { };
description = ''
InvenTree config options.
See [the documentation](https://docs.inventree.org/en/stable/start/config/) for available options.
'';
example = {
INVENTREE_CACHE_ENABLED = true;
INVENTREE_CACHE_HOST = "localhost";
INVENTREE_EMAIL_HOST = "smtp.example.com";
INVENTREE_EMAIL_PORT = 25;
};
};
};
config = lib.mkIf cfg.enable (
lib.mkMerge [
{
services.inventree.settings = {
INVENTREE_DB_ENGINE = cfg.database.dbtype;
INVENTREE_DB_NAME = cfg.database.dbname;
INVENTREE_DB_HOST = cfg.database.dbhost;
INVENTREE_DB_USER = cfg.database.dbuser;
INVENTREE_DB_PORT = toString cfg.database.dbport;
INVENTREE_CONFIG_FILE = lib.mkDefault "${cfg.dataDir}/config/config.yaml";
INVENTREE_OIDC_PRIVATE_KEY_FILE = lib.mkDefault "${cfg.dataDir}/config/oidc_private_key.txt";
INVENTREE_STATIC_ROOT = lib.mkDefault "${cfg.dataDir}/data/static_root";
INVENTREE_MEDIA_ROOT = lib.mkDefault "${cfg.dataDir}/data/media";
INVENTREE_BACKUP_DIR = lib.mkDefault "${cfg.dataDir}/data/backups";
INVENTREE_SITE_URL = lib.mkDefault "http://${cfg.domain}";
INVENTREE_PLUGIN_FILE = lib.mkDefault "${cfg.dataDir}/data/plugins/plugins.txt";
INVENTREE_PLUGIN_DIR = lib.mkDefault "${cfg.dataDir}/data/plugins";
INVENTREE_ADMIN_PASSWORD_FILE = lib.mkDefault cfg.adminPasswordFile;
INVENTREE_SECRET_KEY_FILE = lib.mkDefault cfg.secretKeyFile;
INVENTREE_AUTO_UPDATE = lib.mkDefault "false";
};
environment.systemPackages = [ manage ];
systemd.tmpfiles.rules = (
map (dir: "d ${dir} 0755 inventree inventree") [
"${cfg.dataDir}"
"${cfg.dataDir}/config"
"${cfg.dataDir}/data"
"${cfg.dataDir}/data/static_root"
"${cfg.dataDir}/data/media"
"${cfg.dataDir}/data/backups"
"${cfg.dataDir}/data/plugins"
]
);
services.postgresql = lib.mkIf pgsqlLocal {
enable = true;
ensureDatabases = [ cfg.database.dbname ];
ensureUsers = [
{
name = cfg.database.dbuser;
ensureDBOwnership = true;
}
];
};
services.mysql = lib.mkIf mysqlLocal {
enable = true;
package = lib.mkDefault pkgs.mariadb;
ensureDatabases = [ cfg.database.dbname ];
ensureUsers = [
{
name = cfg.database.dbuser;
ensurePermissions = {
"${cfg.database.dbname}.*" = "ALL PRIVILEGES";
};
}
];
};
services.nginx.enable = true;
services.nginx.virtualHosts.${cfg.domain} = {
locations =
let
unixPath = config.systemd.sockets.inventree-server.socketConfig.ListenStream;
in
{
"/" = {
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Forwarded-By $server_addr:$server_port;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header CLIENT_IP $remote_addr;
proxy_pass_request_headers on;
proxy_redirect off;
client_max_body_size 100M;
proxy_buffering off;
proxy_request_buffering off;
'';
proxyPass = "http://unix:${unixPath}";
};
"/auth" = {
extraConfig = ''
internal;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
'';
proxyPass = "http://unix:${unixPath}:/auth/";
};
"/static/" = {
alias = "${cfg.settings.INVENTREE_STATIC_ROOT}/";
extraConfig = ''
autoindex on;
# Caching settings
expires 30d;
add_header Pragma public;
add_header Cache-Control "public";
'';
};
"/media/" = {
alias = "${cfg.settings.INVENTREE_MEDIA_ROOT}/";
extraConfig = ''
auth_request /auth;
add_header Content-disposition "attachment";
'';
};
};
};
systemd.services.inventree-setup = {
description = "Inventree setup";
wantedBy = [ "inventree.target" ];
partOf = [ "inventree.target" ];
after = lib.optional mysqlLocal "mysql.service" ++ lib.optional pgsqlLocal "postgresql.target";
requires = lib.optional mysqlLocal "mysql.service" ++ lib.optional pgsqlLocal "postgresql.target";
before = [
"inventree-static.service"
"inventree-server.service"
"inventree-qcluster.service"
];
serviceConfig = {
Type = "oneshot";
User = cfg.user;
Group = cfg.group;
RemainAfterExit = true;
PrivateTmp = true;
}
// lib.optionalAttrs (cfg.database.passwordFile != null) {
LoadCredential = "db_password:${cfg.database.passwordFile}";
};
environment = cfg.settings;
script = ''
set -euo pipefail
umask u=rwx,g=,o=
${
lib.optionalString (cfg.database.passwordFile != null) ''
INVENTREE_DB_PASSWORD=$(<"$CREDENTIALS_DIRECTORY/db_password")
''
} \
exec ${pkg}/bin/inventree migrate
'';
};
systemd.services.inventree-static = {
description = "Inventree static migration";
wantedBy = [ "inventree.target" ];
partOf = [ "inventree.target" ];
before = [ "inventree-server.service" ];
environment = cfg.settings;
serviceConfig = {
User = cfg.user;
Group = cfg.group;
StateDirectory = "inventree";
PrivateTmp = true;
}
// lib.optionalAttrs (cfg.database.passwordFile != null) {
LoadCredential = "db_password:${cfg.database.passwordFile}";
};
script = ''
${
lib.optionalString (cfg.database.passwordFile != null) ''
INVENTREE_DB_PASSWORD=$(<"$CREDENTIALS_DIRECTORY/db_password")
''
} \
exec ${pkg}/bin/inventree collectstatic --no-input
'';
};
systemd.services.inventree-server = {
description = "Inventree Gunicorn service";
requiredBy = [ "inventree.target" ];
partOf = [ "inventree.target" ];
environment = cfg.settings;
serviceConfig = {
User = cfg.user;
Group = cfg.group;
StateDirectory = "inventree";
PrivateTmp = true;
}
// lib.optionalAttrs (cfg.database.passwordFile != null) {
LoadCredential = "db_password:${cfg.database.passwordFile}";
};
script = ''
${
lib.optionalString (cfg.database.passwordFile != null) ''
INVENTREE_DB_PASSWORD=$(<"$CREDENTIALS_DIRECTORY/db_password")
''
} \
exec ${pkg}/bin/gunicorn InvenTree.wsgi
'';
};
systemd.sockets.inventree-server = {
wantedBy = [ "sockets.target" ];
partOf = [ "inventree.target" ];
socketConfig.ListenStream = "/run/inventree/gunicorn.socket";
};
systemd.services.inventree-qcluster = {
description = "InvenTree qcluster server";
requiredBy = [ "inventree.target" ];
wantedBy = [ "inventree.target" ];
partOf = [ "inventree.target" ];
environment = cfg.settings;
serviceConfig = {
User = cfg.user;
Group = cfg.group;
StateDirectory = "inventree";
PrivateTmp = true;
}
// lib.optionalAttrs (cfg.database.passwordFile != null) {
LoadCredential = "db_password:${cfg.database.passwordFile}";
};
script = ''
${
lib.optionalString (cfg.database.passwordFile != null) ''
INVENTREE_DB_PASSWORD=$(<"$CREDENTIALS_DIRECTORY/db_password")
''
} \
exec ${pkg}/bin/inventree qcluster
'';
};
systemd.targets.inventree = {
description = "Target for all InvenTree services";
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
};
users = lib.optionalAttrs (cfg.user == cfg.user) {
users.${cfg.user} = {
group = cfg.group;
isSystemUser = true;
home = cfg.dataDir;
};
groups.${cfg.group}.members = [ cfg.user ];
};
}
]
);
}

View file

@ -790,6 +790,7 @@ in
installer = handleTest ./installer.nix { };
installer-systemd-stage-1 = handleTest ./installer-systemd-stage-1.nix { };
intune = runTest ./intune.nix;
inventree = runTest ./inventree.nix;
invidious = runTest ./invidious.nix;
invoiceplane = runTest ./invoiceplane.nix;
iodine = runTest ./iodine.nix;

33
nixos/tests/inventree.nix Normal file
View file

@ -0,0 +1,33 @@
{ lib, ... }:
{
name = "inventree";
meta.maintainers = with lib.maintainers; [
kurogeek
];
nodes = {
psqlTest = {
services.inventree = {
enable = true;
};
};
mysqlTest = {
services.inventree = {
enable = true;
database.dbtype = "mysql";
};
};
};
testScript = ''
start_all()
psqlTest.wait_for_unit("inventree.target")
psqlTest.wait_for_unit("inventree-server.service")
psqlTest.wait_for_open_unix_socket("/run/inventree/gunicorn.socket")
psqlTest.wait_until_succeeds("curl -sf http://localhost/web")
mysqlTest.wait_for_unit("inventree.target")
mysqlTest.wait_for_unit("inventree-server.service")
mysqlTest.wait_for_open_unix_socket("/run/inventree/gunicorn.socket")
mysqlTest.wait_until_succeeds("curl -sf http://localhost/web")
'';
}

View file

@ -0,0 +1,291 @@
{
fetchFromGitHub,
fetchYarnDeps,
lib,
nodejs,
python312,
stdenv,
yarnBuildHook,
yarnConfigHook,
yarnInstallHook,
}:
let
python3 = python312.override {
self = python3;
packageOverrides = self: super: {
django = super.django_4;
};
};
version = "1.1.10";
src = fetchFromGitHub {
owner = "inventree";
repo = "inventree";
tag = "${version}";
hash = "sha256-TPB/3pFIU+ui4c+CbqIKTyAfJ/Xepm/RIhZeYhTrgI4=";
};
frontend =
let
frontendSource = src + "/src/frontend";
in
stdenv.mkDerivation (finalAttrs: {
pname = "inventree-frontend";
inherit version;
src = frontendSource;
yarnOfflineCache = fetchYarnDeps {
yarnLock = finalAttrs.src + "/yarn.lock";
hash = "sha256-Ijbkx+INZgsvMhkzo8h/FUY75W3UHnKAdUjQRD8kJZw=";
};
nativeBuildInputs = [
nodejs
yarnConfigHook
yarnBuildHook
yarnInstallHook
];
buildPhase = ''
mkdir -p $out
export PATH=$PATH:$TMP/frontend/node_modules/.bin
substituteInPlace $TMP/frontend/vite.config.ts --replace-warn "../../src/backend/InvenTree/web/static/web" "$out/static/web"
npm run extract
npm run compile
npm run build
'';
});
in
python3.pkgs.buildPythonApplication rec {
pname = "inventree";
pyproject = true;
inherit version src;
dependencies =
with python3.pkgs;
[
django
dj-rest-auth
django-allauth
django-cleanup
django-cors-headers
django-crispy-forms
django-dbbackup
django-error-report-2
django-filter
django-flags
django-formtools
django-ical
django-import-export
django-maintenance-mode
django-markdownify
django-money
django-mptt
django-mailbox
django-anymail
django-q2
django-redis
django-sesame
django-sql-utils
django-sslserver
django-stdimage
django-storages
django-structlog
django-taggit
django-oauth-toolkit
django-otp
django-user-sessions
django-weasyprint
standard-imghdr
django-xforwardedfor-middleware
djangorestframework-simplejwt
djangorestframework
drf-spectacular
bleach
cryptography
distutils
dulwich
feedparser
gunicorn
jinja2
pdf2image
pillow
pint
python-barcode
python-dotenv
qrcode
pytz
pyyaml
rapidfuzz
sentry-sdk
structlog
tablib
tinycss2
weasyprint
whitenoise
pypdf
ppf-datamatrix
psycopg2
mysqlclient
requests-mock
opentelemetry-api
opentelemetry-sdk
opentelemetry-exporter-otlp
opentelemetry-instrumentation-django
opentelemetry-instrumentation-requests
opentelemetry-instrumentation-redis
opentelemetry-instrumentation-sqlite3
opentelemetry-instrumentation-system-metrics
opentelemetry-instrumentation-wsgi
]
++ tablib.optional-dependencies.all
++ tablib.optional-dependencies.xls
++ tablib.optional-dependencies.xlsx
++ djangorestframework-simplejwt.optional-dependencies.crypto
++ django-anymail.optional-dependencies.amazon-ses
++ django-allauth.optional-dependencies.socialaccount
++ django-allauth.optional-dependencies.saml
++ django-allauth.optional-dependencies.openid
++ django-allauth.optional-dependencies.mfa;
build-system = [ python3.pkgs.setuptools ];
prePatch =
let
skippedCheckFunctions = [
"test_task_check_for_updates"
"test_download_image"
"test_commit_info"
"test_rates"
"test_download_build_orders"
"test_valid_url"
"test_refresh_endpoint"
"test_download_csv"
"test_download_line_items"
"test_export"
"test_download_xlsx"
"test_download_csv"
"test_export"
"test_part_label_translation"
"test_part_download"
"test_date_filters"
"test_bom_export"
"test_hash"
"test_date"
"test_api_call"
"test_function_errors"
"test_stocktake_exporter"
"test_return"
"test_plugin_install"
"test_full_process"
"test_package_loading"
"test_export"
"test_users_exist"
"test_import_part"
"test_model_names"
];
skippedFuncScripts = builtins.map (funcName: ''
grep -rlZ ${funcName} . | while IFS= read -r -d "" file; do
substituteInPlace "$file" --replace-fail "${funcName}" "skip_${funcName}"
done
'') skippedCheckFunctions;
in
''
${lib.concatStringsSep "\n" skippedFuncScripts}
'';
installPhase =
let
pythonPath = python3.pkgs.makePythonPath dependencies;
in
''
runHook preInstall
# Don't need to bother with a non-maintained library from ages ago
substituteInPlace src/backend/InvenTree/InvenTree/settings.py --replace-fail "django_slowtests.testrunner.DiscoverSlowestTestsRunner" "django.test.runner.DiscoverRunner"
mkdir -p $out/lib/${pname}/src/backend/InvenTree/web/
cp -r src $out/lib/${pname}
ln -s ${frontend}/static $out/lib/${pname}/src/backend/InvenTree/web
# cp -r ${frontend}/static $out/lib/${pname}/src/backend/InvenTree/web
chmod +x $out/lib/${pname}/src/backend/InvenTree/manage.py
makeWrapper $out/lib/${pname}/src/backend/InvenTree/manage.py $out/bin/${pname} \
--prefix PYTHONPATH : "${pythonPath}:$out/lib/${pname}/src/backend/InvenTree"
makeWrapper ${lib.getExe python3.pkgs.gunicorn} $out/bin/gunicorn \
--prefix PYTHONPATH : "${pythonPath}:$out/${python3.sitePackages}":"${pythonPath}:$out/lib/${pname}/src/backend/InvenTree"
runHook postInstall
'';
doCheck = true;
env = {
DJANGO_SETTINGS_MODULE = "InvenTree.settings";
};
checkPhase = ''
runHook preCheck
tmpDir=$(mktemp -d)
mkdir -p $tmpDir/media
mkdir -p $tmpDir/.cache/fontconfig
export HOME=$tmpDir
export INVENTREE_STATIC_ROOT=$tmpDir
export INVENTREE_MEDIA_ROOT=$tmpDir/media
export INVENTREE_BACKUP_DIR=$tmpDir
export INVENTREE_DB_ENGINE=django.db.backends.sqlite3
export INVENTREE_DB_NAME=inventree.db
export INVENTREE_SITE_URL="http://localhost:8000"
export INVENTREE_PLUGINS_ENABLED=true
export INVENTREE_PLUGIN_TESTING=true
export INVENTREE_PLUGIN_TESTING_SETUP=true
pushd src/backend/InvenTree
${python3.interpreter} ./manage.py check
${python3.interpreter} ./manage.py migrate
${python3.interpreter} ./manage.py test --failfast
popd
runHook postCheck
'';
nativeCheckInputs = with python3.pkgs; [
django-test-migrations
pytest-django
pytest-env
pytestCheckHook
invoke
coverage
pytest-cov
pdfminer-six
];
passthru =
let
pythonPath = python3.pkgs.makePythonPath dependencies;
in
{
inherit frontend;
pythonPath = pythonPath;
};
meta = {
description = "Open Source Inventory Management System";
homepage = "https://inventree.org/";
changelog = "https://github.com/paperless-ngx/paperless-ngx/releases/tag/${src.tag}";
license = lib.licenses.mit;
platforms = lib.platforms.linux;
mainProgram = "inventree";
maintainers = with lib.maintainers; [
kurogeek
];
};
}

View file

@ -0,0 +1,80 @@
{
buildPythonPackage,
coverage,
django,
django-storages,
fetchFromGitHub,
flake8,
gnupg,
lib,
pep8,
psycopg2,
pylint,
pytest-django,
pytestCheckHook,
python-dotenv,
python-gnupg,
pytz,
setuptools,
testfixtures,
tox,
pythonOlder,
}:
buildPythonPackage rec {
pname = "django-dbbackup";
version = "4.3.0";
pyproject = true;
src = fetchFromGitHub {
owner = "django-dbbackup";
repo = "django-dbbackup";
tag = version;
hash = "sha256-w+LfU5I7swnCJpwqBqoCTRUCZjKoIxK3OC+8CrihLEI=";
};
disabled = pythonOlder "3.9";
dependencies = [
django
python-gnupg
pytz
];
build-system = [ setuptools ];
doCheck = true;
preCheck = ''
tempDir=$(mktemp -d)
export HOME=$tempDir
export DJANGO_SETTINGS_MODULE=dbbackup.tests.settings
'';
pythonImportsCheck = [ "dbbackup" ];
disabledTestPaths = [
# Specific gnupg version required, which aren't provided in upstream
"dbbackup/tests/commands/test_dbrestore.py::DbrestoreCommandRestoreBackupTest::test_decrypt"
"dbbackup/tests/test_connectors/test_base.py::BaseCommandDBConnectorTest::test_run_command_with_parent_env"
"dbbackup/tests/test_utils.py::Encrypt_FileTest::test_func"
"dbbackup/tests/test_utils.py::Compress_FileTest::test_func"
];
nativeCheckInputs = [
coverage
django-storages
flake8
gnupg
pep8
psycopg2
pylint
pytest-django
pytestCheckHook
python-dotenv
testfixtures
tox
];
meta = with lib; {
description = "Management commands to help backup and restore your project database and media files";
homepage = "https://github.com/Archmonger/django-dbbackup";
changelog = "https://github.com/Archmonger/django-dbbackup/releases/tag/${version}";
license = licenses.bsd3;
maintainers = with maintainers; [ kurogeek ];
};
}

View file

@ -0,0 +1,39 @@
{
buildPythonPackage,
django,
fetchFromGitHub,
lib,
setuptools,
pythonOlder,
}:
buildPythonPackage rec {
pname = "django-error-report-2";
version = "0.4.2";
pyproject = true;
src = fetchFromGitHub {
owner = "matmair";
repo = "django-error-report-2";
tag = version;
hash = "sha256-ZCaslqgruJxM8345/jSlZGruM+27H9hvwL0wtPkUzc0=";
};
disabled = pythonOlder "3.6";
dependencies = [
django
];
build-system = [ setuptools ];
# There is no tests on upstream
doCheck = false;
pythonImportsCheck = [ "error_report" ];
meta = with lib; {
description = "Log/View Django server errors.";
homepage = "https://github.com/matmair/django-error-report-2";
changelog = "https://github.com/matmair/django-error-report-2/releases/tag/${version}";
license = licenses.mit;
maintainers = with maintainers; [ kurogeek ];
};
}

View file

@ -0,0 +1,63 @@
{
buildPythonPackage,
coverage,
django,
django-debug-toolbar,
fetchFromGitHub,
jinja2,
lib,
pytest-django,
pytestCheckHook,
setuptools-scm,
setuptools,
pythonOlder,
}:
buildPythonPackage rec {
pname = "django-flags";
version = "5.0.14";
pyproject = true;
src = fetchFromGitHub {
owner = "cfpb";
repo = "django-flags";
tag = version;
hash = "sha256-0IOcpl8OamNlalqNqMvmx/bkuIkaNnLwCD7nFclR8S4=";
};
dependencies = [
django
];
disabled = pythonOlder "3.8";
build-system = [
setuptools
setuptools-scm
];
doCheck = true;
preCheck = ''
export DJANGO_SETTINGS_MODULE=flags.tests.settings
'';
pythonImportsCheck = [ "flags" ];
nativeCheckInputs = [
coverage
(django-debug-toolbar.overrideAttrs (old: rec {
version = "5.2.0";
src = old.src.override {
tag = version;
hash = "sha256-/oWirfJaiHVRI1m3N1QveutX2sag8fjYqJYCZ8BnMa0=";
};
}))
jinja2
pytest-django
pytestCheckHook
];
meta = with lib; {
description = "Feature flags for Django projects";
homepage = "https://github.com/cfpb/django-flags";
changelog = "https://github.com/cfpb/django-flags/releases/tag/${version}";
license = licenses.cc0;
maintainers = with maintainers; [ kurogeek ];
};
}

View file

@ -0,0 +1,61 @@
{
buildPythonPackage,
django,
fetchFromGitHub,
lib,
pytest-django,
pytestCheckHook,
setuptools-scm,
icalendar,
django-recurrence,
pythonOlder,
}:
buildPythonPackage rec {
pname = "django-ical";
version = "1.9.2";
pyproject = true;
src = fetchFromGitHub {
owner = "jazzband";
repo = "django-ical";
tag = version;
hash = "sha256-DUe0loayGcUS7MTyLn+g0KBxbIY7VsaoQNHGSMbMI3U=";
};
disabled = pythonOlder "3.7";
dependencies = [
django
django-recurrence
# Latest version didn't pass the test
(icalendar.overrideAttrs (old: rec {
version = "6.0.0";
src = old.src.override {
tag = "v${version}";
hash = "sha256-eWFDY/pNVfcUk3PfB0vXqh9swuSGtflUw44IMDJI+yI=";
};
}))
];
build-system = [ setuptools-scm ];
doCheck = true;
preCheck = ''
export DJANGO_SETTINGS_MODULE=test_settings
'';
pythonImportsCheck = [
"icalendar"
"django_ical"
];
nativeCheckInputs = [
pytest-django
pytestCheckHook
];
meta = with lib; {
description = "iCal feeds for Django based on Django's syndication feed framework.";
homepage = "https://github.com/jazzband/django-ical";
changelog = "https://github.com/jazzband/django-ical/releases/tag/${version}";
license = licenses.mit;
maintainers = with maintainers; [ kurogeek ];
};
}

View file

@ -0,0 +1,50 @@
{
buildPythonPackage,
django,
fetchFromGitHub,
lib,
pytest-django,
pytestCheckHook,
setuptools,
six,
pythonOlder,
}:
buildPythonPackage rec {
pname = "django-mailbox";
version = "4.10.1";
pyproject = true;
src = fetchFromGitHub {
owner = "coddingtonbear";
repo = "django-mailbox";
tag = version;
hash = "sha256-7CBUnqveTSfdc+8x8sZUqvwYW3vKjZKfOPVWFSo4es0=";
};
disabled = pythonOlder "3.8";
dependencies = [
django
six
];
build-system = [ setuptools ];
doCheck = true;
preCheck = ''
substituteInPlace setup.cfg --replace-fail "pytest" "tool:pytest"
export DJANGO_SETTINGS_MODULE=django_mailbox.tests.settings
'';
pythonImportsCheck = [ "django_mailbox" ];
nativeCheckInputs = [
pytest-django
pytestCheckHook
];
meta = with lib; {
description = "Import mail from POP3, IMAP, local email mailboxes or directly from Postfix or Exim4 into your Django application automatically.";
homepage = "https://github.com/coddingtonbear/django-mailbox";
changelog = "https://github.com/coddingtonbear/django-mailbox/releases/tag/${version}";
license = licenses.mit;
maintainers = with maintainers; [ kurogeek ];
};
}

View file

@ -0,0 +1,53 @@
{
buildPythonPackage,
django,
fetchFromGitHub,
lib,
pytest-django,
pytestCheckHook,
setuptools,
markdown,
bleach,
tinycss2,
}:
buildPythonPackage rec {
pname = "django-markdownify";
version = "0.9.5";
pyproject = true;
src = fetchFromGitHub {
owner = "erwinmatijsen";
repo = "django-markdownify";
tag = version;
hash = "sha256-KYU8p8NRD4EIS/KhOk9nvmXCf0RWEc+IFZ57YtsDSWE=";
};
dependencies = [
django
markdown
bleach
];
build-system = [ setuptools ];
doCheck = true;
preCheck = ''
export DJANGO_SETTINGS_MODULE=markdownify.checks
'';
nativeCheckInputs = [
tinycss2
pytest-django
pytestCheckHook
];
pythonImportsCheck = [ "markdownify" ];
disabledTests = [
# Test settings didn't setup DjangoTemplates
"test_markdownify_nodelist"
];
meta = with lib; {
description = "Markdown template filter for Django";
homepage = "https://github.com/erwinmatijsen/django-markdownify";
license = licenses.mit;
maintainers = with maintainers; [ kurogeek ];
};
}

View file

@ -0,0 +1,55 @@
{
fetchFromGitHub,
buildPythonPackage,
setuptools,
lib,
django,
py-moneyed,
certifi,
pytestCheckHook,
pytest-django,
pytest-cov,
pythonOlder,
}:
buildPythonPackage rec {
pname = "django-money";
version = "3.5.4";
pyproject = true;
src = fetchFromGitHub {
owner = "django-money";
repo = "django-money";
tag = version;
hash = "sha256-JqAZaiJ2zCb7Jwvumqi16IrQ6clmcw71WpPzbhE2Fms=";
};
disabled = pythonOlder "3.7";
dependencies = [
django
certifi
py-moneyed
];
build-system = [ setuptools ];
doCheck = true;
nativeCheckInputs = [
pytestCheckHook
pytest-django
pytest-cov
];
pythonImportsCheck = [ "djmoney" ];
# avoid tests which import mixer, an abandoned library
disabledTests = [
"test_mixer_blend"
];
meta = with lib; {
description = "Money fields for Django forms and models.";
homepage = "https://github.com/django-money/django-money";
changelog = "https://github.com/django-money/django-money/releases/tag/${version}";
license = licenses.bsd3;
maintainers = with maintainers; [ kurogeek ];
};
}

View file

@ -0,0 +1,50 @@
{
buildPythonPackage,
django,
fetchFromGitHub,
lib,
python-dateutil,
pytest-django,
pytestCheckHook,
pytest-cov,
pytest-sugar,
setuptools-scm,
pythonOlder,
}:
buildPythonPackage rec {
pname = "django-recurrence";
version = "1.11.1";
pyproject = true;
src = fetchFromGitHub {
owner = "jazzband";
repo = "django-recurrence";
tag = version;
hash = "sha256-Ytf4fFTVFIQ+6IBhwRMtCkonP0POivv4TrYok37nghA=";
};
disabled = pythonOlder "3.7";
dependencies = [
django
python-dateutil
];
build-system = [ setuptools-scm ];
doCheck = true;
pythonImportsCheck = [ "recurrence" ];
nativeCheckInputs = [
pytest-django
pytest-cov
pytest-sugar
pytestCheckHook
];
meta = with lib; {
description = "Utility for working with recurring dates in Django.";
homepage = "https://github.com/jazzband/django-recurrence";
changelog = "https://github.com/jazzband/django-recurrence/releases/tag/${version}";
license = licenses.bsd3;
maintainers = with maintainers; [ kurogeek ];
};
}

View file

@ -0,0 +1,34 @@
{
buildPythonPackage,
django,
lib,
fetchurl,
setuptools-scm,
pythonOlder,
}:
buildPythonPackage {
pname = "django-sslserver";
version = "0.22";
format = "wheel";
src = fetchurl {
url = "https://files.pythonhosted.org/packages/6f/97/e4011f3944f83a7d2aaaf893c3689ad70e8d2ae46fb6e14fd0e3b0c6ce0b/django_sslserver-0.22-py3-none-any.whl";
hash = "sha256-xZijY9LM3CQhwI3bPYsJc/gOjkejpbdOSiiW8hwpR8U=";
};
disabled = pythonOlder "3.4";
dependencies = [
django
];
build-system = [ setuptools-scm ];
doCheck = true;
meta = with lib; {
description = "A SSL-enabled development server for Django";
homepage = "https://github.com/teddziuba/django-sslserver";
license = licenses.mit;
maintainers = with maintainers; [ kurogeek ];
};
}

View file

@ -0,0 +1,63 @@
{
buildPythonPackage,
django,
fetchFromGitHub,
lib,
pytest-django,
pytestCheckHook,
setuptools-scm,
pillow,
pytest-cov,
gettext,
pythonOlder,
pythonAtLeast,
}:
buildPythonPackage rec {
pname = "django-stdimage";
version = "6.0.2";
pyproject = true;
src = fetchFromGitHub {
owner = "codingjoe";
repo = "django-stdimage";
tag = version;
hash = "sha256-uwVU3Huc5fitAweShJjcMW//GBeIpJcxqKKLGo/EdIs=";
};
disabled = pythonOlder "3.8" || pythonAtLeast "3.13";
dependencies = [
django
pillow
];
build-system = [ setuptools-scm ];
nativeBuildInputs = [ gettext ];
doCheck = true;
preCheck = ''
export DJANGO_SETTINGS_MODULE=tests.settings
'';
disabledTests = [
# SuspiciousFileOperation: Detected path traversal attempt (Even appear in upstream)
"test_variations_override"
];
pythonImportsCheck = [
"stdimage"
"stdimage.validators"
"stdimage.models"
];
nativeCheckInputs = [
pytest-django
pytest-cov
pytestCheckHook
];
meta = with lib; {
description = "Django Standardized Image Field";
homepage = "https://github.com/codingjoe/django-stdimage";
changelog = "https://github.com/codingjoe/django-stdimage/releases/tag/${version}";
license = licenses.mit;
maintainers = with maintainers; [ kurogeek ];
};
}

View file

@ -0,0 +1,82 @@
{
buildPythonPackage,
fetchFromGitHub,
lib,
setuptools,
python,
pkgs,
pythonOlder,
}:
buildPythonPackage rec {
pname = "django-structlog";
version = "9.1.1";
pyproject = true;
src = fetchFromGitHub {
owner = "jrobichaud";
repo = "django-structlog";
tag = version;
hash = "sha256-SEigOdlXZtfLAgRgGkv/eDNDAiiHd7YthRJ/H6e1v5U=";
};
disabled = pythonOlder "3.9";
dependencies = with python.pkgs; [
colorama
django
django-allauth
crispy-bootstrap5
django-crispy-forms
django-environ
django-extensions
django-ipware
django-model-utils
django-ninja
django-redis
djangorestframework
structlog
];
optional-dependencies.celery = with python.pkgs; [ celery ];
build-system = [ setuptools ];
doCheck = true;
preCheck = ''
export DJANGO_SETTINGS_MODULE=config.settings.test_demo_app
${pkgs.valkey}/bin/redis-server &
REDIS_PID=$!
'';
postCheck = ''
kill $REDIS_PID
'';
pytestFlags = [
"-x"
"--cov=./django_structlog_demo_project"
"--cov-append django_structlog_demo_project"
];
pythonImportsCheck = [
"structlog"
"django_structlog"
];
nativeCheckInputs = with python.pkgs; [
celery
factory-boy
pytest-asyncio
pytest-cov
pytest-django
pytest-mock
pytest-sugar
pytestCheckHook
];
meta = with lib; {
description = "Structured Logging for Django";
homepage = "https://github.com/jrobichaud/django-structlog";
changelog = "https://github.com/jrobichaud/django-structlog/releases/tag/${version}";
license = licenses.mit;
maintainers = with maintainers; [ kurogeek ];
};
}

View file

@ -0,0 +1,35 @@
{
fetchFromGitHub,
buildPythonPackage,
setuptools-scm,
lib,
django,
pythonOlder,
}:
buildPythonPackage rec {
pname = "django-user-sessions";
version = "2.0.0";
pyproject = true;
src = fetchFromGitHub {
owner = "jazzband";
repo = "django-user-sessions";
tag = version;
hash = "sha256-Wexy6G2pZ8LTnqtJkBZIePV7qhQW8gu/mKiQfZtgf/o=";
};
disabled = pythonOlder "3.7";
dependencies = [
django
];
build-system = [ setuptools-scm ];
meta = with lib; {
description = "Extend Django sessions with a foreign key back to the user, allowing enumerating all user's sessions.";
homepage = "https://github.com/jazzband/django-user-sessions";
license = licenses.mit;
maintainers = with maintainers; [ kurogeek ];
};
}

View file

@ -0,0 +1,33 @@
{
buildPythonPackage,
django,
fetchFromGitHub,
lib,
setuptools,
}:
buildPythonPackage rec {
pname = "django-xforwardedfor-middleware";
version = "2.0";
pyproject = true;
src = fetchFromGitHub {
owner = "allo-";
repo = "django-xforwardedfor-middleware";
tag = "v${version}";
hash = "sha256-dDXSb17kXOSeIgY6wid1QFHhUjrapasWgCEb/El51eA=";
};
dependencies = [
django
];
build-system = [ setuptools ];
doCheck = true;
meta = with lib; {
description = "Use the X-Forwarded-For header to get the real ip of a request";
homepage = "https://github.com/allo-/django-xforwardedfor-middleware";
license = licenses.publicDomain;
maintainers = with maintainers; [ kurogeek ];
};
}

View file

@ -0,0 +1,38 @@
{
buildPythonPackage,
pythonOlder,
hatchling,
opentelemetry-instrumentation,
opentelemetry-instrumentation-dbapi,
opentelemetry-test-utils,
pytestCheckHook,
}:
buildPythonPackage {
inherit (opentelemetry-instrumentation) version src;
pname = "opentelemetry-instrumentation-sqlite3";
pyproject = true;
disabled = pythonOlder "3.8";
sourceRoot = "${opentelemetry-instrumentation.src.name}/instrumentation/opentelemetry-instrumentation-sqlite3";
build-system = [ hatchling ];
dependencies = [
opentelemetry-instrumentation
opentelemetry-instrumentation-dbapi
];
nativeCheckInputs = [
opentelemetry-test-utils
pytestCheckHook
];
pythonImportsCheck = [ "opentelemetry.instrumentation.sqlite3" ];
meta = opentelemetry-instrumentation.meta // {
homepage = "https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/instrumentation/opentelemetry-instrumentation-sqlite3";
description = "OpenTelemetry Instrumentation for Django";
};
}

View file

@ -0,0 +1,40 @@
{
buildPythonPackage,
pythonOlder,
hatchling,
opentelemetry-instrumentation,
opentelemetry-api,
opentelemetry-test-utils,
psutil,
pytestCheckHook,
}:
buildPythonPackage {
inherit (opentelemetry-instrumentation) version src;
pname = "opentelemetry-instrumentation-system-metrics";
pyproject = true;
disabled = pythonOlder "3.8";
sourceRoot = "${opentelemetry-instrumentation.src.name}/instrumentation/opentelemetry-instrumentation-system-metrics";
build-system = [ hatchling ];
dependencies = [
opentelemetry-instrumentation
opentelemetry-api
psutil
];
nativeCheckInputs = [
opentelemetry-test-utils
pytestCheckHook
];
pythonImportsCheck = [ "opentelemetry.instrumentation.system_metrics" ];
meta = opentelemetry-instrumentation.meta // {
homepage = "https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/instrumentation/opentelemetry-instrumentation-system-metrics";
description = "OpenTelemetry Instrumentation for Django";
};
}

View file

@ -0,0 +1,34 @@
{
lib,
buildPythonPackage,
setuptools-scm,
pythonOlder,
fetchPypi,
pytestCheckHook,
}:
buildPythonPackage rec {
pname = "ppf-datamatrix";
version = "0.2";
format = "pyproject";
disabled = pythonOlder "3.6";
src = fetchPypi {
inherit pname version;
hash = "sha256-jwNNnJDkCPYPixCic7qrgQFMmoHJg9wevcMdTKWsVYI=";
};
doCheck = true;
pythonImportsCheck = [ "ppf.datamatrix" ];
nativeCheckInputs = [ pytestCheckHook ];
build-system = [ setuptools-scm ];
meta = {
description = "Pure-python package to generate data matrix codes.";
homepage = "https://github.com/adrianschlatter/ppf.datamatrix";
license = lib.licenses.mit;
maintainers = with lib.maintainers; [ kurogeek ];
};
}

View file

@ -0,0 +1,38 @@
{
lib,
buildPythonPackage,
fetchPypi,
setuptools,
babel,
pythonOlder,
typing-extensions,
}:
buildPythonPackage rec {
pname = "py-moneyed";
version = "3.0";
format = "pyproject";
disabled = pythonOlder "3.7";
src = fetchPypi {
inherit pname version;
hash = "sha256-SQbw8CzyuR7bouFW8tTpp48iQFmrjI+i/yYjDHXYlOg=";
};
dependencies = [
babel
typing-extensions
];
pythonImportsCheck = [ "moneyed" ];
build-system = [ setuptools ];
meta = {
description = "Provides Currency and Money classes for use in your Python code.";
homepage = "https://github.com/py-moneyed/py-moneyed";
license = lib.licenses.bsd3;
maintainers = with lib.maintainers; [ kurogeek ];
};
}

View file

@ -4158,6 +4158,8 @@ self: super: with self; {
django-currentuser = callPackage ../development/python-modules/django-currentuser { };
django-dbbackup = callPackage ../development/python-modules/django-dbbackup { };
django-debug-toolbar = callPackage ../development/python-modules/django-debug-toolbar { };
django-dynamic-preferences =
@ -4172,6 +4174,8 @@ self: super: with self; {
django-environ = callPackage ../development/python-modules/django-environ { };
django-error-report-2 = callPackage ../development/python-modules/django-error-report-2 { };
django-extensions = callPackage ../development/python-modules/django-extensions { };
django-filer = callPackage ../development/python-modules/django-filer { };
@ -4180,6 +4184,8 @@ self: super: with self; {
django-filter = callPackage ../development/python-modules/django-filter { };
django-flags = callPackage ../development/python-modules/django-flags { };
django-formset-js-improved =
callPackage ../development/python-modules/django-formset-js-improved
{ };
@ -4214,6 +4220,8 @@ self: super: with self; {
django-i18nfield = callPackage ../development/python-modules/django-i18nfield { };
django-ical = callPackage ../development/python-modules/django-ical { };
django-import-export = callPackage ../development/python-modules/django-import-export { };
django-ipware = callPackage ../development/python-modules/django-ipware { };
@ -4242,10 +4250,14 @@ self: super: with self; {
callPackage ../development/python-modules/django-login-required-middleware
{ };
django-mailbox = callPackage ../development/python-modules/django-mailbox { };
django-mailman3 = callPackage ../development/python-modules/django-mailman3 { };
django-maintenance-mode = callPackage ../development/python-modules/django-maintenance-mode { };
django-markdownify = callPackage ../development/python-modules/django-markdownify { };
django-markdownx = callPackage ../development/python-modules/django-markdownx { };
django-markup = callPackage ../development/python-modules/django-markup { };
@ -4264,6 +4276,8 @@ self: super: with self; {
django-modeltranslation = callPackage ../development/python-modules/django-modeltranslation { };
django-money = callPackage ../development/python-modules/django-money { };
django-mptt = callPackage ../development/python-modules/django-mptt { };
django-multiselectfield = callPackage ../development/python-modules/django-multiselectfield { };
@ -4332,6 +4346,8 @@ self: super: with self; {
django-ratelimit = callPackage ../development/python-modules/django-ratelimit { };
django-recurrence = callPackage ../development/python-modules/django-recurrence { };
django-redis = callPackage ../development/python-modules/django-redis { };
django-registration = callPackage ../development/python-modules/django-registration { };
@ -4374,10 +4390,19 @@ self: super: with self; {
django-sql-utils = callPackage ../development/python-modules/django-sql-utils { };
django-sslserver = callPackage ../development/python-modules/django-sslserver { };
django-statici18n = callPackage ../development/python-modules/django-statici18n { };
django-stdimage = callPackage ../development/python-modules/django-stdimage {
django = django_4;
pytest-django = pytest-django.override { django = django_4; };
};
django-storages = callPackage ../development/python-modules/django-storages { };
django-structlog = callPackage ../development/python-modules/django-structlog { };
django-stubs = callPackage ../development/python-modules/django-stubs { };
django-stubs-ext = callPackage ../development/python-modules/django-stubs-ext { };
@ -4410,6 +4435,8 @@ self: super: with self; {
django-types = callPackage ../development/python-modules/django-types { };
django-user-sessions = callPackage ../development/python-modules/django-user-sessions { };
django-valkey = callPackage ../development/python-modules/django-valkey { };
django-versatileimagefield =
@ -4426,6 +4453,10 @@ self: super: with self; {
django-widget-tweaks = callPackage ../development/python-modules/django-widget-tweaks { };
django-xforwardedfor-middleware =
callPackage ../development/python-modules/django-xforwardedfor-middleware
{ };
# LTS in extended support phase
django_4 = callPackage ../development/python-modules/django/4.nix { };
@ -11694,6 +11725,14 @@ self: super: with self; {
callPackage ../development/python-modules/opentelemetry-instrumentation-sqlalchemy
{ };
opentelemetry-instrumentation-sqlite3 =
callPackage ../development/python-modules/opentelemetry-instrumentation-sqlite3
{ };
opentelemetry-instrumentation-system-metrics =
callPackage ../development/python-modules/opentelemetry-instrumentation-system-metrics
{ };
opentelemetry-instrumentation-threading =
callPackage ../development/python-modules/opentelemetry-instrumentation-threading
{ };
@ -12715,6 +12754,8 @@ self: super: with self; {
ppdeep = callPackage ../development/python-modules/ppdeep { };
ppf-datamatrix = callPackage ../development/python-modules/ppf-datamatrix { };
ppft = callPackage ../development/python-modules/ppft { };
ppk2-api = callPackage ../development/python-modules/ppk2-api { };
@ -13078,6 +13119,8 @@ self: super: with self; {
py-melissa-climate = callPackage ../development/python-modules/py-melissa-climate { };
py-moneyed = callPackage ../development/python-modules/py-moneyed { };
py-multiaddr = callPackage ../development/python-modules/py-multiaddr { };
py-multibase = callPackage ../development/python-modules/py-multibase { };