diff --git a/pkgs/by-name/pd/pdfding/frontend.nix b/pkgs/by-name/pd/pdfding/frontend.nix new file mode 100644 index 000000000000..753db30af5e6 --- /dev/null +++ b/pkgs/by-name/pd/pdfding/frontend.nix @@ -0,0 +1,87 @@ +{ + stdenv, + nodejs, + npmHooks, + fetchpatch2, + fetchNpmDeps, + fetchzip, + fetchFromGitHub, + tailwindcss_4, +}: +let + pdfjsVersion = "5.4.296"; # see update script + pdfjsHash = "sha256-b4W7wETq2CIZm2rJCmXEYvPhQtCbXS76L7GDvng6wn4="; + pdfjs = fetchzip { + url = "https://github.com/mozilla/pdf.js/releases/download/v${pdfjsVersion}/pdfjs-${pdfjsVersion}-dist.zip"; + hash = pdfjsHash; + stripRoot = false; + postFetch = '' + rm -rf $out/web/locale \ + $out/web/standard_fonts \ + $out/web/compressed.tracemonkey-pldi-09.pdf + + # remove source maps + find "$out" -name '*.map' -exec rm -f '{}' \; + ''; + }; +in +stdenv.mkDerivation (finalAttrs: { + pname = "pdfding-frontend"; + version = "1.4.1"; + src = fetchFromGitHub { + owner = "mrmn2"; + repo = "PdfDing"; + tag = "v${finalAttrs.version}"; + hash = "sha256-8e80gMdB6U3977dIU7bIAAEguYmi+AWQgUgYPDLCYLI="; + }; + + npmDeps = fetchNpmDeps { + inherit (finalAttrs) src; + name = "pdfding-frontend-${finalAttrs.version}-npm-deps"; + hash = "sha256-v1NFqDnFcRK8sd0bV3ck+LLMYQ90Dl1R1OnBTwWUVUg="; + }; + + patches = [ + # remove patch in 1.4.2 + # package.json has missing version and name + (fetchpatch2 { + url = "https://github.com/mrmn2/PdfDing/pull/203.patch?full_index=1"; + hash = "sha256-lKtpqKdyoGZdU4fTegto+YUIduIWbM82RQU9459NpC0="; + }) + ]; + + nativeBuildInputs = [ + nodejs + npmHooks.npmConfigHook + # it is in package.json and thus node_modules but no cli executable + tailwindcss_4 + ]; + + # keeping the file structure same as upstream to minimise confusion + buildPhase = '' + runHook preBuild + mkdir -p $out/pdfding + cp -r --no-preserve=mode pdfding/static $out/pdfding/static + cp -r --no-preserve=mode ${finalAttrs.passthru.pdfjs} $out/pdfding/static/pdfjs + + tailwindcss -i $out/pdfding/static/css/input.css -o $out/pdfding/static/css/tailwind.css --minify + rm $out/pdfding/static/css/input.css + + for i in build/pdf.mjs build/pdf.sandbox.mjs build/pdf.worker.mjs web/viewer.mjs; \ + do node_modules/terser/bin/terser $out/pdfding/static/pdfjs/$i --compress -o $out/pdfding/static/pdfjs/$i; done + + npm run build + + cp -r pdfding/static/js $out/pdfding/static + + runHook postBuild + ''; + + passthru = { + inherit pdfjs; + }; + + meta = { + description = "PdfDing frontend"; + }; +}) diff --git a/pkgs/by-name/pd/pdfding/package.nix b/pkgs/by-name/pd/pdfding/package.nix new file mode 100644 index 000000000000..7e7715851396 --- /dev/null +++ b/pkgs/by-name/pd/pdfding/package.nix @@ -0,0 +1,214 @@ +{ + lib, + python3, + callPackage, + fetchFromGitHub, + fetchpatch2, + makeWrapper, +}: +let + python = python3; +in +python.pkgs.buildPythonPackage (finalAttrs: { + pname = "pdfding"; + version = "1.4.1"; + src = fetchFromGitHub { + owner = "mrmn2"; + repo = "PdfDing"; + tag = "v${finalAttrs.version}"; + hash = "sha256-rrUaqxDO16NAOic74jeYgN+7Alvo+fIIacJdSOg0hFM="; + # remove in 1.5.0 + postFetch = "mv $out/{license.txt,LICENSE}"; + }; + pyproject = true; + + patches = [ + # remove all patches in 1.5.0 (next version after 1.4.1) + # patch to add data_dir + (fetchpatch2 { + url = "https://github.com/mrmn2/PdfDing/commit/f4945f2836ca8d972fcee2f00ef1d9cf217bada1.patch?full_index=1"; + hash = "sha256-VGjyIAVi+qd2WZ8FVKKC2ijLinoflO7RmPwIW1/oGcY="; + }) + # pyproject.toml still has 0.1.1 very old version + (fetchpatch2 { + url = "https://github.com/mrmn2/PdfDing/pull/203.patch?full_index=1"; + hash = "sha256-lKtpqKdyoGZdU4fTegto+YUIduIWbM82RQU9459NpC0="; + }) + # allows customising consume_schedule crontab + (fetchpatch2 { + url = "https://github.com/mrmn2/PdfDing/commit/96a13574718e0d27240eee8893fb799a02f24c05.patch?full_index=1"; + hash = "sha256-Stq392rIbsphvaE23GgFWb91KzpD6aOQu9MGDDoaO7s="; + }) + # allow specifying region for s3 backups + (fetchpatch2 { + url = "https://github.com/mrmn2/PdfDing/commit/3e412654f62d83b745111bd1d3587aca1a7739e1.patch?full_index=1"; + hash = "sha256-RQS3yJjrIaViFlm6it6zyRZOn+nTQE8qr8OpY+zYSCY="; + }) + ]; + + # remove supervisor from dependencies + postPatch = '' + sed -i 's/supervisor.*$//' pyproject.toml + + substituteInPlace pdfding/backup/tests/test_management.py pdfding/backup/tests/test_tasks.py \ + --replace-fail "Path(__file__).parents[2]" "Path('$PDFDING_OUT_DIR')" + ''; + + dependencies = + with python.pkgs; + [ + django + django-allauth + django-cleanup + django-htmx + gunicorn + markdown + minio + nh3 + psycopg2-binary + pypdf + pypdfium2 + python-magic + qrcode + rapidfuzz + ruamel-yaml + whitenoise + huey + pillow + oauthlib + + # dependecies required for django collectstatic + requests + pyjwt + cryptography + ] + ++ qrcode.optional-dependencies.pil + ++ django-allauth.optional-dependencies.socialaccount; + + build-system = with python.pkgs; [ poetry-core ]; + + nativeBuildInputs = [ + makeWrapper + ]; + + optional-dependencies = { + e2e = with python.pkgs; [ + pytest + pytest-django + pytest-playwright + pytest-rerunfailures # required to retry some flaky e2e tests + ]; + }; + + preBuild = '' + # remove originals, copy from frontend + rm -rf pdfding/static + ln -s ${finalAttrs.passthru.frontend}/pdfding/static pdfding/static + + # staticfiles step requires prod configuration, remove dev.py + mv pdfding/core/settings/dev.py dev.py.bak + + ${python.pythonOnBuildForHost.interpreter} pdfding/manage.py collectstatic + + # not needed, now we have staticfiles directory + rm -rf pdfding/static + + # the following is from upstream's Dockerfile + + # remove django md5 hash from filenames of pdfjs as it will mess up the relative imports because of the whitenoise setup + export PDFJS_PATH="pdfding/staticfiles/pdfjs" + for file_name in $(find $PDFJS_PATH -type f -not -path "$PDFJS_PATH/web/images/*") + do + if [[ $file_name =~ "LICENSE" ]]; then + new=$(echo "$file_name" | sed -E "s/LICENSE\.[a-zA-Z0-9]{12}/LICENSE/"); + else + new=$(echo "$file_name" | sed -E "s/\.[a-zA-Z0-9]{12}\./\./"); + fi; + mv -- "$file_name" "$new"; + done \ + && echo 'Successfully removed hash from pdfjs files' + + echo "VERSION = '${finalAttrs.version}'" > pdfding/core/settings/version.py; + ''; + + env.PDFDING_OUT_DIR = "${placeholder "out"}/${python.sitePackages}/pdfding"; + + makeWrapperArgs = [ + "--set-default DATA_DIR /var/lib/pdfding" + # allow for gunicorn processes to have access to Python packages + "--prefix PYTHONPATH : " + "${python.pkgs.makePythonPath finalAttrs.passthru.dependencies}:${finalAttrs.env.PDFDING_OUT_DIR}" + ]; + + postInstall = '' + mkdir -p $out/bin + + makeWrapper "$PDFDING_OUT_DIR/manage.py" $out/bin/pdfding-manage \ + $makeWrapperArgs + + makeWrapper ${lib.getExe python.pkgs.gunicorn} $out/bin/pdfding-start \ + --add-flags '--bind ''${HOST_IP:-127.0.0.1}:''${HOST_PORT:-8080} core.wsgi:application' \ + $makeWrapperArgs + ''; + + pythonRelaxDeps = [ + "django" + "django-allauth" + "huey" + "minio" + "pypdf" + "pypdfium2" + ]; + + nativeCheckInputs = with python.pkgs; [ + pytest-django + pytestCheckHook + ]; + + # from .github/workflows/tests.yaml + pytestFlags = [ + "--ignore=e2e" + ]; + + /* + fix two breaking tests by providing full out path + AssertionError: Calls not found + AssertionError: 'add_file_to_minio' does not contain all of ... + */ + preCheck = '' + # dev.py is required for tests, restore it + mv dev.py.bak $PDFDING_OUT_DIR/core/settings/dev.py + + # tests should run in pdfding directory + pushd pdfding + ''; + + postCheck = '' + # come out of the pdfding directory + popd + + # remove dev.py + rm $PDFDING_OUT_DIR/core/settings/dev.py + ''; + + pythonImportsCheck = [ + "pdfding" + ]; + + passthru = { + inherit python; + frontend = callPackage ./frontend.nix { }; + updateScript = ./update.sh; + }; + + meta = { + changelog = "https://github.com/mrmn2/PdfDing/blob/${finalAttrs.src.rev}/CHANGELOG.md"; + description = "Selfhosted PDF manager, viewer and editor offering a seamless user experience on multiple devices"; + downloadPage = "https://github.com/mrmn2/PdfDing"; + homepage = "https://pdfding.com"; + license = lib.licenses.agpl3Only; + mainProgram = "pdfding-manage"; + maintainers = with lib.maintainers; [ phanirithvij ]; + teams = with lib.teams; [ ngi ]; + }; +}) diff --git a/pkgs/by-name/pd/pdfding/update.sh b/pkgs/by-name/pd/pdfding/update.sh new file mode 100755 index 000000000000..debf815b4e70 --- /dev/null +++ b/pkgs/by-name/pd/pdfding/update.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i bash -p curl jq nix-update gitMinimal prefetch-npm-deps coreutils + +set -x +set -eou pipefail + +version=$(curl ${GITHUB_TOKEN:+ -H "Authorization: Bearer $GITHUB_TOKEN"} -sL https://api.github.com/repos/mrmn2/PdfDing/releases/latest | jq -r '.tag_name') + +if [[ "v${UPDATE_NIX_OLD_VERSION:-}" == "$version" ]]; then + echo "Already up-to-date, version: $version" + exit 0 +fi + +# source hashes +nix-update --version="$version" pdfding +nix-update --version="$version" pdfding.frontend + +NIXPKGS_PATH="$(git rev-parse --show-toplevel)" +PACKAGE_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" + +TMPDIR="$(mktemp -d)" +trap 'rm -rf "$TMPDIR"' EXIT +cd "$TMPDIR" + +src="$(nix-build --no-link "$NIXPKGS_PATH" -A pdfding.src)" +cp "$src"/{package.json,package-lock.json} . + +# npmDeps hash +prev_npm_hash="$( + nix-instantiate "$NIXPKGS_PATH" \ + --eval --json \ + -A pdfding.frontend.npmDeps.hash | + jq -r . +)" +new_npm_hash="$(prefetch-npm-deps ./package-lock.json)" + +sed -i "s|$prev_npm_hash|$new_npm_hash|g" "$PACKAGE_DIR/frontend.nix" + +# pdfjs version +pdfjs_version="$(grep 'PDFJS_VERSION=' "$src/Dockerfile" | cut -d'=' -f2)" + +sed -i "s|pdfjsVersion = .*;|pdfjsVersion = \"$pdfjs_version\";|" "$PACKAGE_DIR/frontend.nix" + +# pdfjs hash +sed -i "s|pdfjsHash = .*;|pdfjsHash = \"sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\";|" "$PACKAGE_DIR/frontend.nix" + +set +e +new_pdfjs_hash="$( + nix-build --no-out-link -A pdfding.frontend.pdfjs "$NIXPKGS_PATH" 2>&1 >/dev/null | grep "got:" | cut -d':' -f2 | sed 's| ||g' +)" +set -e + +sed -i "s|\"sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\"|\"$new_pdfjs_hash\"|g" "$PACKAGE_DIR/frontend.nix"