Merge pull request #296180 from mweinelt/pretix

pretix: init at 2024.2.0

Changed files
+1363 -32
.github
nixos
doc
manual
release-notes
modules
services
web-apps
tests
pkgs
+5
.github/CODEOWNERS
···
nixos/modules/services/continuous-integration/buildbot @Mic92 @zowoq
nixos/tests/buildbot.nix @Mic92 @zowoq
pkgs/development/tools/continuous-integration/buildbot @Mic92 @zowoq
+
+
# Pretix
+
pkgs/by-name/pr/pretix/ @mweinelt
+
nixos/modules/services/web-apps/pretix.nix @mweinelt
+
nixos/tests/web-apps/pretix.nix @mweinelt
+2
nixos/doc/manual/release-notes/rl-2405.section.md
···
- [Monado](https://monado.freedesktop.org/), an open source XR runtime. Available as [services.monado](#opt-services.monado.enable).
+
- [Pretix](https://pretix.eu/about/en/), an open source ticketing software for events. Available as [services.pretix]($opt-services-pretix.enable).
+
- [Clevis](https://github.com/latchset/clevis), a pluggable framework for automated decryption, used to unlock encrypted devices in initrd. Available as [boot.initrd.clevis.enable](#opt-boot.initrd.clevis.enable).
- [armagetronad](https://wiki.armagetronad.org), a mid-2000s 3D lightcycle game widely played at iD Tech Camps. You can define multiple servers using `services.armagetronad.<server>.enable`.
+1
nixos/modules/module-list.nix
···
./services/web-apps/plausible.nix
./services/web-apps/powerdns-admin.nix
./services/web-apps/pretalx.nix
+
./services/web-apps/pretix.nix
./services/web-apps/prosody-filer.nix
./services/web-apps/rimgo.nix
./services/web-apps/sftpgo.nix
+579
nixos/modules/services/web-apps/pretix.nix
···
+
{ config
+
, lib
+
, pkgs
+
, utils
+
, ...
+
}:
+
+
let
+
inherit (lib)
+
concatMapStringsSep
+
escapeShellArgs
+
filter
+
filterAttrs
+
getExe
+
getExe'
+
isAttrs
+
isList
+
literalExpression
+
mapAttrs
+
mkDefault
+
mkEnableOption
+
mkIf
+
mkOption
+
mkPackageOption
+
optionals
+
optionalString
+
recursiveUpdate
+
types
+
;
+
+
filterRecursiveNull = o:
+
if isAttrs o then
+
mapAttrs (_: v: filterRecursiveNull v) (filterAttrs (_: v: v != null) o)
+
else if isList o then
+
map filterRecursiveNull (filter (v: v != null) o)
+
else
+
o;
+
+
cfg = config.services.pretix;
+
format = pkgs.formats.ini { };
+
+
configFile = format.generate "pretix.cfg" (filterRecursiveNull cfg.settings);
+
+
finalPackage = cfg.package.override {
+
inherit (cfg) plugins;
+
};
+
+
pythonEnv = cfg.package.python.buildEnv.override {
+
extraLibs = with cfg.package.python.pkgs; [
+
(toPythonModule finalPackage)
+
gunicorn
+
]
+
++ lib.optionals (cfg.settings.memcached.location != null)
+
cfg.package.optional-dependencies.memcached
+
;
+
};
+
+
withRedis = cfg.settings.redis.location != null;
+
in
+
{
+
meta = with lib; {
+
maintainers = with maintainers; [ hexa ];
+
};
+
+
options.services.pretix = {
+
enable = mkEnableOption "pretix";
+
+
package = mkPackageOption pkgs "pretix" { };
+
+
group = mkOption {
+
type = types.str;
+
default = "pretix";
+
description = ''
+
Group under which pretix should run.
+
'';
+
};
+
+
user = mkOption {
+
type = types.str;
+
default = "pretix";
+
description = ''
+
User under which pretix should run.
+
'';
+
};
+
+
environmentFile = mkOption {
+
type = types.nullOr types.path;
+
default = null;
+
example = "/run/keys/pretix-secrets.env";
+
description = ''
+
Environment file to pass secret configuration values.
+
+
Each line must follow the `PRETIX_SECTION_KEY=value` pattern.
+
'';
+
};
+
+
plugins = mkOption {
+
type = types.listOf types.package;
+
default = [];
+
example = literalExpression ''
+
with config.services.pretix.package.plugins; [
+
passbook
+
pages
+
];
+
'';
+
description = ''
+
Pretix plugins to install into the Python environment.
+
'';
+
};
+
+
gunicorn.extraArgs = mkOption {
+
type = with types; listOf str;
+
default = [
+
"--name=pretix"
+
];
+
example = [
+
"--name=pretix"
+
"--workers=4"
+
"--max-requests=1200"
+
"--max-requests-jitter=50"
+
"--log-level=info"
+
];
+
description = ''
+
Extra arguments to pass to gunicorn.
+
See <https://docs.pretix.eu/en/latest/admin/installation/manual_smallscale.html#start-pretix-as-a-service> for details.
+
'';
+
apply = escapeShellArgs;
+
};
+
+
celery = {
+
extraArgs = mkOption {
+
type = with types; listOf str;
+
default = [ ];
+
description = ''
+
Extra arguments to pass to celery.
+
+
See <https://docs.celeryq.dev/en/stable/reference/cli.html#celery-worker> for more info.
+
'';
+
apply = utils.escapeSystemdExecArgs;
+
};
+
};
+
+
nginx = {
+
enable = mkOption {
+
type = types.bool;
+
default = true;
+
example = false;
+
description = ''
+
Whether to set up an nginx virtual host.
+
'';
+
};
+
+
domain = mkOption {
+
type = types.str;
+
example = "talks.example.com";
+
description = ''
+
The domain name under which to set up the virtual host.
+
'';
+
};
+
};
+
+
database.createLocally = mkOption {
+
type = types.bool;
+
default = true;
+
example = false;
+
description = ''
+
Whether to automatically set up the database on the local DBMS instance.
+
+
Only supported for PostgreSQL. Not required for sqlite.
+
'';
+
};
+
+
settings = mkOption {
+
type = types.submodule {
+
freeformType = format.type;
+
options = {
+
pretix = {
+
instance_name = mkOption {
+
type = types.str;
+
example = "tickets.example.com";
+
description = ''
+
The name of this installation.
+
'';
+
};
+
+
url = mkOption {
+
type = types.str;
+
example = "https://tickets.example.com";
+
description = ''
+
The installation’s full URL, without a trailing slash.
+
'';
+
};
+
+
cachedir = mkOption {
+
type = types.path;
+
default = "/var/cache/pretix";
+
description = ''
+
Directory for storing temporary files.
+
'';
+
};
+
+
datadir = mkOption {
+
type = types.path;
+
default = "/var/lib/pretix";
+
description = ''
+
Directory for storing user uploads and similar data.
+
'';
+
};
+
+
logdir = mkOption {
+
type = types.path;
+
default = "/var/log/pretix";
+
description = ''
+
Directory for storing log files.
+
'';
+
};
+
+
currency = mkOption {
+
type = types.str;
+
default = "EUR";
+
example = "USD";
+
description = ''
+
Default currency for events in its ISO 4217 three-letter code.
+
'';
+
};
+
+
registration = mkOption {
+
type = types.bool;
+
default = false;
+
example = true;
+
description = ''
+
Whether to allow registration of new admin users.
+
'';
+
};
+
};
+
+
database = {
+
backend = mkOption {
+
type = types.enum [
+
"sqlite3"
+
"postgresql"
+
];
+
default = "postgresql";
+
description = ''
+
Database backend to use.
+
+
Only postgresql is recommended for production setups.
+
'';
+
};
+
+
host = mkOption {
+
type = with types; nullOr types.path;
+
default = if cfg.settings.database.backend == "postgresql" then "/run/postgresql" else null;
+
defaultText = literalExpression ''
+
if config.services.pretix.settings..database.backend == "postgresql" then "/run/postgresql"
+
else null
+
'';
+
description = ''
+
Database host or socket path.
+
'';
+
};
+
+
name = mkOption {
+
type = types.str;
+
default = "pretix";
+
description = ''
+
Database name.
+
'';
+
};
+
+
user = mkOption {
+
type = types.str;
+
default = "pretix";
+
description = ''
+
Database username.
+
'';
+
};
+
};
+
+
mail = {
+
from = mkOption {
+
type = types.str;
+
example = "tickets@example.com";
+
description = ''
+
E-Mail address used in the `FROM` header of outgoing mails.
+
'';
+
};
+
+
host = mkOption {
+
type = types.str;
+
default = "localhost";
+
example = "mail.example.com";
+
description = ''
+
Hostname of the SMTP server use for mail delivery.
+
'';
+
};
+
+
port = mkOption {
+
type = types.port;
+
default = 25;
+
example = 587;
+
description = ''
+
Port of the SMTP server to use for mail delivery.
+
'';
+
};
+
};
+
+
celery = {
+
backend = mkOption {
+
type = types.str;
+
default = "redis+socket://${config.services.redis.servers.pretix.unixSocket}?virtual_host=1";
+
defaultText = literalExpression ''
+
optionalString config.services.pretix.celery.enable "redis+socket://''${config.services.redis.servers.pretix.unixSocket}?virtual_host=1"
+
'';
+
description = ''
+
URI to the celery backend used for the asynchronous job queue.
+
'';
+
};
+
+
broker = mkOption {
+
type = types.str;
+
default = "redis+socket://${config.services.redis.servers.pretix.unixSocket}?virtual_host=2";
+
defaultText = literalExpression ''
+
optionalString config.services.pretix.celery.enable "redis+socket://''${config.services.redis.servers.pretix.unixSocket}?virtual_host=2"
+
'';
+
description = ''
+
URI to the celery broker used for the asynchronous job queue.
+
'';
+
};
+
};
+
+
redis = {
+
location = mkOption {
+
type = with types; nullOr str;
+
default = "unix://${config.services.redis.servers.pretix.unixSocket}?db=0";
+
defaultText = literalExpression ''
+
"unix://''${config.services.redis.servers.pretix.unixSocket}?db=0"
+
'';
+
description = ''
+
URI to the redis server, used to speed up locking, caching and session storage.
+
'';
+
};
+
+
sessions = mkOption {
+
type = types.bool;
+
default = true;
+
example = false;
+
description = ''
+
Whether to use redis as the session storage.
+
'';
+
};
+
};
+
+
memcached = {
+
location = mkOption {
+
type = with types; nullOr str;
+
default = null;
+
example = "127.0.0.1:11211";
+
description = ''
+
The `host:port` combination or the path to the UNIX socket of a memcached instance.
+
+
Can be used instead of Redis for caching.
+
'';
+
};
+
};
+
+
tools = {
+
pdftk = mkOption {
+
type = types.path;
+
default = getExe pkgs.pdftk;
+
defaultText = literalExpression ''
+
lib.getExe pkgs.pdftk
+
'';
+
description = ''
+
Path to the pdftk executable.
+
'';
+
};
+
};
+
};
+
};
+
default = { };
+
description = ''
+
pretix configuration as a Nix attribute set. All settings can also be passed
+
from the environment.
+
+
See <https://docs.pretix.eu/en/latest/admin/config.html> for possible options.
+
'';
+
};
+
};
+
+
config = mkIf cfg.enable {
+
# https://docs.pretix.eu/en/latest/admin/installation/index.html
+
+
environment.systemPackages = [
+
(pkgs.writeScriptBin "pretix-manage" ''
+
cd ${cfg.settings.pretix.datadir}
+
sudo=exec
+
if [[ "$USER" != ${cfg.user} ]]; then
+
sudo='exec /run/wrappers/bin/sudo -u ${cfg.user} ${optionalString withRedis "-g redis-pretix"} --preserve-env=PRETIX_CONFIG_FILE'
+
fi
+
export PRETIX_CONFIG_FILE=${configFile}
+
$sudo ${getExe' pythonEnv "pretix-manage"} "$@"
+
'')
+
];
+
+
services = {
+
nginx = mkIf cfg.nginx.enable {
+
enable = true;
+
recommendedGzipSettings = mkDefault true;
+
recommendedOptimisation = mkDefault true;
+
recommendedProxySettings = mkDefault true;
+
recommendedTlsSettings = mkDefault true;
+
upstreams.pretix.servers."unix:/run/pretix/pretix.sock" = { };
+
virtualHosts.${cfg.nginx.domain} = {
+
# https://docs.pretix.eu/en/latest/admin/installation/manual_smallscale.html#ssl
+
extraConfig = ''
+
more_set_headers Referrer-Policy same-origin;
+
more_set_headers X-Content-Type-Options nosniff;
+
'';
+
locations = {
+
"/".proxyPass = "http://pretix";
+
"/media/" = {
+
alias = "${cfg.settings.pretix.datadir}/media/";
+
extraConfig = ''
+
access_log off;
+
expires 7d;
+
'';
+
};
+
"^~ /media/(cachedfiles|invoices)" = {
+
extraConfig = ''
+
deny all;
+
return 404;
+
'';
+
};
+
"/static/" = {
+
alias = "${finalPackage}/${cfg.package.python.sitePackages}/pretix/static.dist/";
+
extraConfig = ''
+
access_log off;
+
more_set_headers Cache-Control "public";
+
expires 365d;
+
'';
+
};
+
};
+
};
+
};
+
+
postgresql = mkIf (cfg.database.createLocally && cfg.settings.database.backend == "postgresql") {
+
enable = true;
+
ensureUsers = [ {
+
name = cfg.settings.database.user;
+
ensureDBOwnership = true;
+
} ];
+
ensureDatabases = [ cfg.settings.database.name ];
+
};
+
+
redis.servers.pretix.enable = withRedis;
+
};
+
+
systemd.services = let
+
commonUnitConfig = {
+
environment.PRETIX_CONFIG_FILE = configFile;
+
serviceConfig = {
+
User = "pretix";
+
Group = "pretix";
+
EnvironmentFile = optionals (cfg.environmentFile != null) [
+
cfg.environmentFile
+
];
+
StateDirectory = [
+
"pretix"
+
];
+
CacheDirectory = "pretix";
+
LogsDirectory = "pretix";
+
WorkingDirectory = cfg.settings.pretix.datadir;
+
SupplementaryGroups = optionals withRedis [
+
"redis-pretix"
+
];
+
AmbientCapabilities = "";
+
CapabilityBoundingSet = [ "" ];
+
DevicePolicy = "closed";
+
LockPersonality = true;
+
MemoryDenyWriteExecute = true;
+
NoNewPrivileges = true;
+
PrivateDevices = true;
+
PrivateTmp = true;
+
ProcSubset = "pid";
+
ProtectControlGroups = true;
+
ProtectHome = true;
+
ProtectHostname = true;
+
ProtectKernelLogs = true;
+
ProtectKernelModules = true;
+
ProtectKernelTunables = true;
+
ProtectProc = "invisible";
+
ProtectSystem = "strict";
+
RemoveIPC = true;
+
RestrictAddressFamilies = [
+
"AF_INET"
+
"AF_INET6"
+
"AF_UNIX"
+
];
+
RestrictNamespaces = true;
+
RestrictRealtime = true;
+
RestrictSUIDSGID = true;
+
SystemCallArchitectures = "native";
+
SystemCallFilter = [
+
"@system-service"
+
"~@privileged"
+
"@chown"
+
];
+
UMask = "0077";
+
};
+
};
+
in {
+
pretix-web = recursiveUpdate commonUnitConfig {
+
description = "pretix web service";
+
after = [
+
"network.target"
+
"redis-pretix.service"
+
"postgresql.service"
+
];
+
wantedBy = [ "multi-user.target" ];
+
preStart = ''
+
versionFile="${cfg.settings.pretix.datadir}/.version"
+
version=$(cat "$versionFile" 2>/dev/null || echo 0)
+
+
pluginsFile="${cfg.settings.pretix.datadir}/.plugins"
+
plugins=$(cat "$pluginsFile" 2>/dev/null || echo "")
+
configuredPlugins="${concatMapStringsSep "|" (package: package.name) cfg.plugins}"
+
+
if [[ $version != ${cfg.package.version} || $plugins != $configuredPlugins ]]; then
+
${getExe' pythonEnv "pretix-manage"} migrate
+
+
echo "${cfg.package.version}" > "$versionFile"
+
echo "$configuredPlugins" > "$pluginsFile"
+
fi
+
'';
+
serviceConfig = {
+
ExecStart = "${getExe' pythonEnv "gunicorn"} --bind unix:/run/pretix/pretix.sock ${cfg.gunicorn.extraArgs} pretix.wsgi";
+
RuntimeDirectory = "pretix";
+
};
+
};
+
+
pretix-periodic = recursiveUpdate commonUnitConfig {
+
description = "pretix periodic task runner";
+
# every 15 minutes
+
startAt = [ "*:3,18,33,48" ];
+
serviceConfig = {
+
Type = "oneshot";
+
ExecStart = "${getExe' pythonEnv "pretix-manage"} runperiodic";
+
};
+
};
+
+
pretix-worker = recursiveUpdate commonUnitConfig {
+
description = "pretix asynchronous job runner";
+
after = [
+
"network.target"
+
"redis-pretix.service"
+
"postgresql.service"
+
];
+
wantedBy = [ "multi-user.target" ];
+
serviceConfig.ExecStart = "${getExe' pythonEnv "celery"} -A pretix.celery_app worker ${cfg.celery.extraArgs}";
+
};
+
};
+
+
systemd.sockets.pretix-web.socketConfig = {
+
ListenStream = "/run/pretix/pretix.sock";
+
SocketUser = "nginx";
+
};
+
+
users = {
+
groups."${cfg.group}" = {};
+
users."${cfg.user}" = {
+
isSystemUser = true;
+
createHome = true;
+
home = cfg.settings.pretix.datadir;
+
inherit (cfg) group;
+
};
+
};
+
};
+
}
+1
nixos/tests/all-tests.nix
···
pppd = handleTest ./pppd.nix {};
predictable-interface-names = handleTest ./predictable-interface-names.nix {};
pretalx = runTest ./web-apps/pretalx.nix;
+
pretix = runTest ./web-apps/pretix.nix;
printing-socket = handleTest ./printing.nix { socket = true; };
printing-service = handleTest ./printing.nix { socket = false; };
privoxy = handleTest ./privoxy.nix {};
+47
nixos/tests/web-apps/pretix.nix
···
+
{
+
lib,
+
pkgs,
+
...
+
}:
+
+
{
+
name = "pretix";
+
meta.maintainers = with lib.maintainers; [ hexa ];
+
+
nodes = {
+
pretix = {
+
networking.extraHosts = ''
+
127.0.0.1 tickets.local
+
'';
+
+
services.pretix = {
+
enable = true;
+
nginx.domain = "tickets.local";
+
plugins = with pkgs.pretix.plugins; [
+
passbook
+
pages
+
];
+
settings = {
+
pretix = {
+
instance_name = "NixOS Test";
+
url = "http://tickets.local";
+
};
+
mail.from = "hello@tickets.local";
+
};
+
};
+
};
+
};
+
+
testScript = ''
+
start_all()
+
+
pretix.wait_for_unit("pretix-web.service")
+
pretix.wait_for_unit("pretix-worker.service")
+
+
pretix.wait_until_succeeds("curl -q --fail http://tickets.local")
+
+
pretix.succeed("pretix-manage --help")
+
+
pretix.log(pretix.succeed("systemd-analyze security pretix-web.service"))
+
'';
+
}
+264
pkgs/by-name/pr/pretix/package.nix
···
+
{ lib
+
, buildNpmPackage
+
, fetchFromGitHub
+
, fetchPypi
+
, fetchpatch2
+
, nodejs
+
, python3
+
, gettext
+
, nixosTests
+
, plugins ? [ ]
+
}:
+
+
let
+
python = python3.override {
+
packageOverrides = self: super: {
+
django = super.django_4;
+
+
stripe = super.stripe.overridePythonAttrs rec {
+
version = "7.9.0";
+
+
src = fetchPypi {
+
pname = "stripe";
+
inherit version;
+
hash = "sha256-hOXkMINaSwzU/SpXzjhTJp0ds0OREc2mtu11LjSc9KE=";
+
};
+
};
+
+
pretix-plugin-build = self.callPackage ./plugin-build.nix { };
+
};
+
};
+
+
pname = "pretix";
+
version = "2024.2.0";
+
+
src = fetchFromGitHub {
+
owner = "pretix";
+
repo = "pretix";
+
rev = "refs/tags/v${version}";
+
hash = "sha256-emtF5dDXEXN8GIucHbjF+m9Vkg1Jj6nmQdHhBOkXMAs=";
+
};
+
+
npmDeps = buildNpmPackage {
+
pname = "pretix-node-modules";
+
inherit version src;
+
+
sourceRoot = "${src.name}/src/pretix/static/npm_dir";
+
npmDepsHash = "sha256-kE13dcTdWZZNHPMcHEiK0a2dEcu3Z3/q815YhaVkLbQ=";
+
+
dontBuild = true;
+
+
installPhase = ''
+
runHook preInstall
+
+
mkdir $out
+
cp -R node_modules $out/
+
+
runHook postInstall
+
'';
+
};
+
in
+
python.pkgs.buildPythonApplication rec {
+
inherit pname version src;
+
pyproject = true;
+
+
patches = [
+
# Discover pretix.plugin entrypoints during build and add them into
+
# INSTALLED_APPS, so that their static files are collected.
+
./plugin-build.patch
+
+
(fetchpatch2 {
+
# Allow customization of cache and log directory
+
# https://github.com/pretix/pretix/pull/3997
+
name = "pretix-directory-customization.patch";
+
url = "https://github.com/pretix/pretix/commit/e151d1d1f08917e547df49da0779b36bb73b7294.patch";
+
hash = "sha256-lO5eCKSqUaCwSm7rouMTFMwauWl9Tz/Yf0JE/IO+bnI=";
+
})
+
];
+
+
postPatch = ''
+
NODE_PREFIX=src/pretix/static.dist/node_prefix
+
mkdir -p $NODE_PREFIX
+
cp -R ${npmDeps}/node_modules $NODE_PREFIX/
+
chmod -R u+w $NODE_PREFIX/
+
+
# unused
+
sed -i "/setuptools-rust/d" pyproject.toml
+
+
substituteInPlace pyproject.toml \
+
--replace-fail phonenumberslite phonenumbers \
+
--replace-fail psycopg2-binary psycopg2 \
+
--replace-fail vat_moss_forked==2020.3.20.0.11.0 vat-moss \
+
--replace-fail "bleach==5.0.*" bleach \
+
--replace-fail "dnspython==2.5.*" dnspython \
+
--replace-fail "importlib_metadata==7.*" importlib_metadata \
+
--replace-fail "protobuf==4.25.*" protobuf \
+
--replace-fail "pycryptodome==3.20.*" pycryptodome \
+
--replace-fail "pypdf==3.9.*" pypdf \
+
--replace-fail "python-dateutil==2.8.*" python-dateutil \
+
--replace-fail "sentry-sdk==1.40.*" sentry-sdk \
+
--replace-fail "stripe==7.9.*" stripe
+
'';
+
+
build-system = with python.pkgs; [
+
gettext
+
nodejs
+
pythonRelaxDepsHook
+
setuptools
+
tomli
+
];
+
+
dependencies = with python.pkgs; [
+
arabic-reshaper
+
babel
+
beautifulsoup4
+
bleach
+
celery
+
chardet
+
cryptography
+
css-inline
+
defusedcsv
+
dj-static
+
django
+
django-bootstrap3
+
django-compressor
+
django-countries
+
django-filter
+
django-formset-js-improved
+
django-formtools
+
django-hierarkey
+
django-hijack
+
django-i18nfield
+
django-libsass
+
django-localflavor
+
django-markup
+
django-oauth-toolkit
+
django-otp
+
django-phonenumber-field
+
django-redis
+
django-scopes
+
django-statici18n
+
djangorestframework
+
dnspython
+
drf-ujson2
+
geoip2
+
importlib-metadata
+
isoweek
+
jsonschema
+
kombu
+
libsass
+
lxml
+
markdown
+
mt-940
+
oauthlib
+
openpyxl
+
packaging
+
paypalrestsdk
+
paypal-checkout-serversdk
+
pyjwt
+
phonenumbers
+
pillow
+
pretix-plugin-build
+
protobuf
+
psycopg2
+
pycountry
+
pycparser
+
pycryptodome
+
pypdf
+
python-bidi
+
python-dateutil
+
pytz
+
pytz-deprecation-shim
+
pyuca
+
qrcode
+
redis
+
reportlab
+
requests
+
sentry-sdk
+
sepaxml
+
slimit
+
static3
+
stripe
+
text-unidecode
+
tlds
+
tqdm
+
vat-moss
+
vobject
+
webauthn
+
zeep
+
] ++ plugins;
+
+
optional-dependencies = with python.pkgs; {
+
memcached = [
+
pylibmc
+
];
+
};
+
+
postInstall = ''
+
mkdir -p $out/bin
+
cp ./src/manage.py $out/bin/pretix-manage
+
+
# Trim packages size
+
rm -rfv $out/${python.sitePackages}/pretix/static.dist/node_prefix
+
'';
+
+
dontStrip = true; # no binaries
+
+
nativeCheckInputs = with python.pkgs; [
+
pytestCheckHook
+
pytest-xdist
+
pytest-mock
+
pytest-django
+
pytest-asyncio
+
pytest-rerunfailures
+
freezegun
+
fakeredis
+
responses
+
] ++ lib.flatten (lib.attrValues optional-dependencies);
+
+
pytestFlagsArray = [
+
"--reruns" "3"
+
+
# tests fail when run before 4:30am
+
# https://github.com/pretix/pretix/pull/3987
+
"--deselect=src/tests/base/test_orders.py::PaymentReminderTests::test_sent_days"
+
"--deselect=src/tests/plugins/sendmail/test_rules.py::test_sendmail_rule_specified_subevent"
+
];
+
+
preCheck = ''
+
export PYTHONPATH=$(pwd)/src:$PYTHONPATH
+
export DJANGO_SETTINGS_MODULE=tests.settings
+
'';
+
+
passthru = {
+
inherit
+
npmDeps
+
python
+
;
+
plugins = lib.recurseIntoAttrs
+
(python.pkgs.callPackage ./plugins {
+
inherit (python.pkgs) callPackage;
+
}
+
);
+
tests = {
+
inherit (nixosTests) pretix;
+
};
+
};
+
+
meta = with lib; {
+
description = "Ticketing software that cares about your event—all the way";
+
homepage = "https://github.com/pretix/pretix";
+
license = with licenses; [
+
agpl3Only
+
# 3rd party components below src/pretix/static
+
bsd2
+
isc
+
mit
+
ofl # fontawesome
+
unlicense
+
# all other files below src/pretix/static and src/pretix/locale and aux scripts
+
asl20
+
];
+
maintainers = with maintainers; [ hexa ];
+
};
+
}
+37
pkgs/by-name/pr/pretix/plugin-build.nix
···
+
{
+
lib,
+
buildPythonPackage,
+
fetchPypi,
+
setuptools,
+
django,
+
gettext,
+
}:
+
+
buildPythonPackage rec {
+
pname = "pretix-plugin-build";
+
version = "1.0.1";
+
pyproject = true;
+
+
src = fetchPypi {
+
inherit pname version;
+
hash = "sha256-iLbqcCAbeK4PyLXiebpdE27rt6bOP7eXczIG2bdvvYo=";
+
};
+
+
build-system = [
+
setuptools
+
];
+
+
dependencies = [
+
django
+
gettext
+
];
+
+
doCheck = false; # no tests
+
+
meta = with lib; {
+
description = "";
+
homepage = "https://github.com/pretix/pretix-plugin-build";
+
license = licenses.asl20;
+
maintainers = with maintainers; [ hexa ];
+
};
+
}
+20
pkgs/by-name/pr/pretix/plugin-build.patch
···
+
diff --git a/src/pretix/_build_settings.py b/src/pretix/_build_settings.py
+
index c03f56a1a..d1ea73b84 100644
+
--- a/src/pretix/_build_settings.py
+
+++ b/src/pretix/_build_settings.py
+
@@ -24,6 +24,8 @@
+
This file contains settings that we need at wheel require time. All settings that we only need at runtime are set
+
in settings.py.
+
"""
+
+from importlib_metadata import entry_points
+
+
+
from ._base_settings import * # NOQA
+
+
ENTROPY = {
+
@@ -47,3 +49,6 @@ HAS_MEMCACHED = False
+
HAS_CELERY = False
+
HAS_GEOIP = False
+
SENTRY_ENABLED = False
+
+
+
+for entry_point in entry_points(group='pretix.plugin'):
+
+ INSTALLED_APPS.append(entry_point.module) # noqa: F405
+13
pkgs/by-name/pr/pretix/plugins/default.nix
···
+
{ callPackage
+
, ...
+
}:
+
+
{
+
pages = callPackage ./pages.nix { };
+
+
passbook = callPackage ./passbook.nix { };
+
+
reluctant-stripe = callPackage ./reluctant-stripe.nix { };
+
+
stretchgoals = callPackage ./stretchgoals.nix { };
+
}
+37
pkgs/by-name/pr/pretix/plugins/pages.nix
···
+
{ lib
+
, buildPythonPackage
+
, fetchFromGitHub
+
, pretix-plugin-build
+
, setuptools
+
}:
+
+
buildPythonPackage rec {
+
pname = "pretix-pages";
+
version = "1.6.0";
+
pyproject = true;
+
+
src = fetchFromGitHub {
+
owner = "pretix";
+
repo = "pretix-pages";
+
rev = "v${version}";
+
hash = "sha256-cO5tAiOifLpqFEQwYgrGoByUecpzvue8YmChpPwm+y0=";
+
};
+
+
build-system = [
+
pretix-plugin-build
+
setuptools
+
];
+
+
doCheck = false; # no tests
+
+
pythonImportsCheck = [
+
"pretix_pages"
+
];
+
+
meta = with lib; {
+
description = "Plugin to add static pages to your pretix event";
+
homepage = "https://github.com/pretix/pretix-pages";
+
license = licenses.asl20;
+
maintainers = with maintainers; [ hexa ];
+
};
+
}
+33
pkgs/by-name/pr/pretix/plugins/passbook-openssl.patch
···
+
diff --git a/pretix_passbook/apps.py b/pretix_passbook/apps.py
+
index e34eee1..a7ad382 100644
+
--- a/pretix_passbook/apps.py
+
+++ b/pretix_passbook/apps.py
+
@@ -22,15 +22,6 @@ class PassbookApp(AppConfig):
+
def ready(self):
+
from . import signals # NOQA
+
+
- @cached_property
+
- def compatibility_errors(self):
+
- import shutil
+
-
+
- errs = []
+
- if not shutil.which("openssl"):
+
- errs.append("The OpenSSL binary is not installed or not in the PATH.")
+
- return errs
+
-
+
@cached_property
+
def compatibility_warnings(self):
+
errs = []
+
diff --git a/pretix_passbook/forms.py b/pretix_passbook/forms.py
+
index 2a38604..aec38de 100644
+
--- a/pretix_passbook/forms.py
+
+++ b/pretix_passbook/forms.py
+
@@ -41,7 +41,7 @@ class CertificateFileField(forms.FileField):
+
return SimpleUploadedFile("cert.pem", content, "text/plain")
+
+
openssl_cmd = [
+
- "openssl",
+
+ "@openssl@",
+
"x509",
+
"-inform",
+
"DER",
+59
pkgs/by-name/pr/pretix/plugins/passbook.nix
···
+
{ lib
+
, buildPythonPackage
+
, fetchFromGitHub
+
, substituteAll
+
+
# build-system
+
, pretix-plugin-build
+
, setuptools
+
+
# runtime
+
, openssl
+
+
# dependencies
+
, googlemaps
+
, wallet-py3k
+
}:
+
+
buildPythonPackage rec {
+
pname = "pretix-passbook";
+
version = "1.13.1";
+
pyproject = true;
+
+
src = fetchFromGitHub {
+
owner = "pretix";
+
repo = "pretix-passbook";
+
rev = "v${version}";
+
hash = "sha256-bp64wCEMon05JhOaDr/cVbqUxc+7ndcsSuSesxJt8GE=";
+
};
+
+
patches = [
+
(substituteAll {
+
src = ./passbook-openssl.patch;
+
openssl = lib.getExe openssl;
+
})
+
];
+
+
build-system = [
+
pretix-plugin-build
+
setuptools
+
];
+
+
dependencies = [
+
googlemaps
+
wallet-py3k
+
];
+
+
doCheck = false; # no tests
+
+
pythonImportsCheck = [
+
"pretix_passbook"
+
];
+
+
meta = with lib; {
+
description = "Support for Apple Wallet/Passbook files in pretix";
+
homepage = "https://github.com/pretix/pretix-passbook";
+
license = licenses.asl20;
+
maintainers = with maintainers; [ hexa ];
+
};
+
}
+37
pkgs/by-name/pr/pretix/plugins/reluctant-stripe.nix
···
+
{ lib
+
, buildPythonPackage
+
, fetchFromGitHub
+
, pretix-plugin-build
+
, setuptools
+
}:
+
+
buildPythonPackage {
+
pname = "pretix-reluctant-stripe";
+
version = "unstable-2023-08-03";
+
pyproject = true;
+
+
src = fetchFromGitHub {
+
owner = "metarheinmain";
+
repo = "pretix-reluctant-stripe";
+
rev = "ae2d770442553e5fc00815ff4521a8fd2c113fd9";
+
hash = "sha256-bw9aDMxl4/uar5KHjj+wwkYkaGMRxHWY/c1N75bxu0o=";
+
};
+
+
build-system = [
+
pretix-plugin-build
+
setuptools
+
];
+
+
doCheck = false; # no tests
+
+
pythonImportsCheck = [
+
"pretix_reluctant_stripe"
+
];
+
+
meta = with lib; {
+
description = "Nudge users to not use Stripe as a payment provider";
+
homepage = "https://github.com/metarheinmain/pretix-reluctant-stripe";
+
license = licenses.asl20;
+
maintainers = with maintainers; [ hexa ];
+
};
+
}
+37
pkgs/by-name/pr/pretix/plugins/stretchgoals.nix
···
+
{ lib
+
, buildPythonPackage
+
, fetchFromGitHub
+
, pretix-plugin-build
+
, setuptools
+
}:
+
+
buildPythonPackage {
+
pname = "pretix-avgchart";
+
version = "unstable-2023-11-27";
+
pyproject = true;
+
+
src = fetchFromGitHub {
+
owner = "rixx";
+
repo = "pretix-avgchart";
+
rev = "219816c7ec523a5c23778523b2616ac0c835cb3a";
+
hash = "sha256-1V/0PUvStgQeBQ0v6GoofAgyPmWs3RD+v5ekmAO9vFU=";
+
};
+
+
build-system = [
+
pretix-plugin-build
+
setuptools
+
];
+
+
doCheck = false; # no tests
+
+
pythonImportsCheck = [
+
"pretix_stretchgoals"
+
];
+
+
meta = with lib; {
+
description = "Display the average ticket sales price over time";
+
homepage = "https://github.com/rixx/pretix-avgchart";
+
license = licenses.asl20;
+
maintainers = with maintainers; [ hexa ];
+
};
+
}
+5
pkgs/development/python-modules/css-inline/default.nix
···
# native darwin dependencies
, libiconv
, Security
+
, SystemConfiguration
# tests
, pytestCheckHook
···
buildInputs = lib.optionals stdenv.isDarwin [
libiconv
Security
+
SystemConfiguration
];
pythonImportsCheck = [
···
disabledTests = [
# fails to connect to local server
"test_remote_stylesheet"
+
] ++ lib.optionals (stdenv.isDarwin) [
+
# pyo3_runtime.PanicException: event loop thread panicked
+
"test_invalid_href"
];
meta = with lib; {
+29 -11
pkgs/development/python-modules/django-compressor/default.nix
···
{ lib
, buildPythonPackage
, fetchPypi
+
, pythonRelaxDepsHook
+
+
# build-system
+
, setuptools
+
+
# dependencies
+
, calmjs
+
, django-appconf
+
, jinja2
, rcssmin
, rjsmin
-
, django-appconf
+
+
# tests
, beautifulsoup4
, brotli
-
, pytestCheckHook
+
, csscompressor
, django-sekizai
+
, pytestCheckHook
, pytest-django
-
, csscompressor
-
, calmjs
-
, jinja2
-
, python
+
}:
buildPythonPackage rec {
pname = "django-compressor";
version = "4.4";
-
format = "setuptools";
+
pyproject = true;
src = fetchPypi {
pname = "django_compressor";
···
hash = "sha256-GwrMnPup9pvDjnxB2psNcKILyVWHtkP/75YJz0YGT2c=";
};
-
propagatedBuildInputs = [
+
build-system = [
+
setuptools
+
pythonRelaxDepsHook
+
];
+
+
pythonRelaxDeps = [
+
"rcssmin"
+
"rjsmin"
+
];
+
+
dependencies = [
beautifulsoup4
calmjs
django-appconf
···
rjsmin
];
-
checkInputs = [
+
env.DJANGO_SETTINGS_MODULE = "compressor.test_settings";
+
+
nativeCheckInputs = [
beautifulsoup4
brotli
csscompressor
···
];
pythonImportsCheck = [ "compressor" ];
-
-
DJANGO_SETTINGS_MODULE = "compressor.test_settings";
meta = with lib; {
description = "Compresses linked and inline JavaScript or CSS into single cached files";
+29 -12
pkgs/development/python-modules/django-otp/default.nix
···
{ lib
, buildPythonPackage
, fetchFromGitHub
+
, hatchling
, django
, freezegun
-
, pythonOlder
, qrcode
+
, pytest
+
, python
}:
buildPythonPackage rec {
pname = "django-otp";
-
version = "1.1.3";
-
format = "setuptools";
-
disabled = pythonOlder "3";
+
version = "1.3.0post1";
+
pyproject = true;
src = fetchFromGitHub {
owner = "django-otp";
repo = "django-otp";
rev = "v${version}";
-
hash = "sha256-Ac9p7q9yaUr3WTTGxCY16Yo/Z8i1RtnD2g0Aj2pqSXY=";
+
hash = "sha256-Q8YTCYERyoAXenSiDabxuxaWiD6ZeJKKKgaR/Rg3y20=";
};
-
postPatch = ''
-
patchShebangs manage.py
-
'';
+
build-system = [
+
hatchling
+
];
-
propagatedBuildInputs = [
+
dependencies = [
django
qrcode
];
+
+
env.DJANGO_SETTINGS_MODUOLE = "test.test_project.settings";
nativeCheckInputs = [
freezegun
+
pytest
];
checkPhase = ''
-
./manage.py test django_otp
+
runHook preCheck
+
+
export PYTHONPATH=$PYTHONPATH:test
+
export DJANGO_SETTINGS_MODULE=test_project.settings
+
${python.interpreter} -m django test django_otp
+
+
runHook postCheck
'';
-
pythonImportsCheck = [ "django_otp" ];
+
pytestFlagsArray = [
+
"src/django_otp/test.py"
+
];
+
+
pythonImportsCheck = [
+
"django_otp"
+
];
meta = with lib; {
-
homepage = "https://github.com/jazzband/django-model-utils";
+
homepage = "https://github.com/django-otp/django-otp";
+
changelog = "https://github.com/django-otp/django-otp/blob/${src.rev}/CHANGES.rst";
description = "Pluggable framework for adding two-factor authentication to Django using one-time passwords";
license = licenses.bsd2;
maintainers = with maintainers; [ ];
+19 -7
pkgs/development/python-modules/django-statici18n/default.nix
···
{ lib
, buildPythonPackage
, fetchFromGitHub
+
+
# build-system
+
, setuptools
+
+
# dependencies
, django
, django-appconf
+
+
# tests
, pytest-django
, pytestCheckHook
}:
buildPythonPackage rec {
pname = "django-statici18n";
-
version = "2.3.1";
-
format = "setuptools";
+
version = "2.4.0";
+
pyproject = true;
src = fetchFromGitHub {
owner = "zyegfryed";
-
repo = pname;
-
rev = "refs/tags/v${version}";
-
hash = "sha256-2fFJJNdF0jspS7djDL8sToPTetzNR6pfNp5ohCNa30I=";
+
repo = "django-statici18n";
+
# https://github.com/zyegfryed/django-statici18n/issues/59
+
rev = "9b83a8f0f2e625dd5f56d53cfe4e07aca9479ab6";
+
hash = "sha256-KrIlWmN7um9ad2avfANOza579bjYkxTo9F0UFpvLu3A=";
};
-
propagatedBuildInputs = [
+
build-system = [
+
setuptools
+
];
+
+
dependencies = [
django
django-appconf
];
···
"statici18n"
];
-
DJANGO_SETTINGS_MODULE = "tests.test_project.project.settings";
+
env.DJANGO_SETTINGS_MODULE = "tests.test_project.project.settings";
nativeCheckInputs = [
pytest-django
+44
pkgs/development/python-modules/paypalrestsdk/default.nix
···
+
{ buildPythonPackage
+
, fetchPypi
+
+
# build-system
+
, setuptools
+
+
# dependencies
+
, pyopenssl
+
, requests
+
, six
+
}:
+
+
buildPythonPackage rec {
+
pname = "paypalrestsdk";
+
version = "1.13.2";
+
pyproject = true;
+
+
src = fetchPypi {
+
inherit pname version;
+
sha256 = "sha256-kZUfNtsw1oW5ceFASYSRo1bPHfjv9xZWYDrKTtcs81o=";
+
};
+
+
build-system = [
+
setuptools
+
];
+
+
dependencies = [
+
pyopenssl
+
requests
+
six
+
];
+
+
doCheck = false; # no tests
+
+
pythonImportsCheck = [
+
"paypalrestsdk"
+
];
+
+
meta = {
+
homepage = "https://developer.paypal.com/";
+
description = "Python APIs to create, process and manage payment";
+
license = "PayPal SDK License";
+
};
+
}
+47
pkgs/development/python-modules/wallet-py3k/default.nix
···
+
{ lib
+
, buildPythonPackage
+
, fetchPypi
+
, substituteAll
+
, openssl
+
, setuptools
+
, six
+
}:
+
+
buildPythonPackage rec {
+
pname = "wallet-py3k";
+
version = "0.0.4";
+
pyproject = true;
+
+
src = fetchPypi {
+
inherit pname version;
+
hash = "sha256-kyHSh8qHbzK6gFLGnL6dUJ/GLJHTNC86jjXa/APqIzI=";
+
};
+
+
patches = [
+
(substituteAll {
+
src = ./openssl-path.patch;
+
openssl = lib.getExe openssl;
+
})
+
];
+
+
build-system = [
+
setuptools
+
];
+
+
dependencies = [
+
six
+
];
+
+
doCheck = false; # no tests
+
+
pythonImportsCheck = [
+
"wallet"
+
];
+
+
meta = with lib; {
+
description = "Passbook file generator";
+
homepage = "https://pypi.org/project/wallet-py3k";
+
license = licenses.mit;
+
maintainers = with maintainers; [ hexa ];
+
};
+
}
+13
pkgs/development/python-modules/wallet-py3k/openssl-path.patch
···
+
diff --git a/wallet/models.py b/wallet/models.py
+
index 1b75402..ad115de 100644
+
--- a/wallet/models.py
+
+++ b/wallet/models.py
+
@@ -320,7 +320,7 @@ class Pass(object):
+
def _createSignature(self, manifest, certificate, key,
+
wwdr_certificate, password):
+
openssl_cmd = [
+
- 'openssl',
+
+ '@openssl@',
+
'smime',
+
'-binary',
+
'-sign',
-1
pkgs/top-level/python-aliases.nix
···
pam = python-pam; # added 2020-09-07.
PasteDeploy = pastedeploy; # added 2021-10-07
pathpy = path; # added 2022-04-12
-
paypalrestsdk = throw "paypalrestsdk was removed, the upstream repo was archived back in 2020"; # Added 2023-11-25
pdfposter = throw "pdfposter was promoted to a top-level attribute"; # Added 2023-06-29
pdfminer = pdfminer-six; # added 2022-05-25
pep257 = pydocstyle; # added 2022-04-12
+5 -1
pkgs/top-level/python-packages.nix
···
css-inline = callPackage ../development/python-modules/css-inline {
inherit (pkgs.darwin) libiconv;
-
inherit (pkgs.darwin.apple_sdk.frameworks) Security;
+
inherit (pkgs.darwin.apple_sdk.frameworks) Security SystemConfiguration;
};
css-parser = callPackage ../development/python-modules/css-parser { };
···
paypal-checkout-serversdk = callPackage ../development/python-modules/paypal-checkout-serversdk { };
paypalhttp = callPackage ../development/python-modules/paypalhttp { };
+
+
paypalrestsdk = callPackage ../development/python-modules/paypalrestsdk { };
pbkdf2 = callPackage ../development/python-modules/pbkdf2 { };
···
wakeonlan = callPackage ../development/python-modules/wakeonlan { };
wallbox = callPackage ../development/python-modules/wallbox { };
+
+
wallet-py3k = callPackage ../development/python-modules/wallet-py3k { };
walrus = callPackage ../development/python-modules/walrus { };