Package Penpot 2.18.3 for Nix with NixOS module; afk devShell
Vendored from debling/nix-penpot (MIT). Fixes on top: - pnpm fetchers: drop --config.fetch-* flags (UND_ERR_INVALID_ARG on pnpm 11.27) and pin real store hashes (frontend x3, exporter, mcp) - deps-lock.json: add transitive funcool/beicon2 v2.0 so fake-git resolves potok2's pin during the shadow-cljs classpath build - expose frontend pnpm stores via passthru for rehashing - x86_64-linux only; devShell ships afk, jj, git
This commit is contained in:
commit
08b5496980
21 files changed
+7840
No files matched your search
@@ -0,0 +1,244 @@
|
||||
# Penpot backend (Clojure API server), packaged as the upstream source
|
||||
# uberjar. Reproduces `backend/scripts/build`: a non-AOT uberjar executed
|
||||
# through `clojure.main`, shipped with the log4j2 config and the onboarding
|
||||
# template files mapped from the pinned penpot-files flake input.
|
||||
#
|
||||
# The uberjar is repacked by packages/jar-strip-deterministic.py (in the penpot
|
||||
# checkout), replacing stripJavaArchivesHook, whose reorder is O(n^2) and took
|
||||
# ~32 minutes on this jar.
|
||||
#
|
||||
# Output contract:
|
||||
# $out/share/penpot-backend/{penpot.jar,log4j2.xml,version.txt,manage.py}
|
||||
# $out/share/penpot-backend/builtin-templates/
|
||||
# $out/bin/penpot-backend (java wrapper, cwd = share dir)
|
||||
# $out/bin/penpot-manage (manage.py CLI via python3 + tabulate)
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
makeWrapper,
|
||||
callPackage,
|
||||
clojure,
|
||||
jdk25_headless,
|
||||
babashka,
|
||||
imagemagick,
|
||||
fontforge,
|
||||
woff2,
|
||||
fontconfig,
|
||||
util-linux,
|
||||
python3,
|
||||
penpot,
|
||||
penpot-files,
|
||||
version,
|
||||
subSrc,
|
||||
}:
|
||||
|
||||
let
|
||||
src = subSrc penpot [
|
||||
"backend"
|
||||
"common"
|
||||
"CHANGES.md"
|
||||
];
|
||||
|
||||
# Replaces stripJavaArchivesHook: a stdlib-python deterministic repack of the
|
||||
# uberjar, replicating strip-nondeterminism's jar/zip handlers (see the
|
||||
# script's docstring for the full list). Its per-member reorder is O(n^2),
|
||||
# which cost ~32 minutes on this jar's 54000 entries; this does it in ~20 s.
|
||||
jarNormalizer = ./jar-strip-deterministic.py;
|
||||
in
|
||||
let
|
||||
# sfnt2woff/woff2sfnt are not in nixpkgs; see backend-woff-tools.nix.
|
||||
woff-tools = callPackage ./backend-woff-tools.nix { };
|
||||
|
||||
# The clojure CLI with a JDK matching the runtime (upstream run.sh flags
|
||||
# need >= 24; the nixpkgs default CLI bundles JDK 21).
|
||||
clojureJdk25 = clojure.override { jdk = jdk25_headless; };
|
||||
|
||||
magickPolicy = callPackage ./imagemagick-policy.nix { inherit penpot; };
|
||||
|
||||
pythonEnv = python3.withPackages (ps: [ ps.tabulate ]);
|
||||
|
||||
# NOTE: upstream's docker image runs Zulu JDK 26 at 2.17.x; any JDK >= 24
|
||||
# satisfies the run.sh flags, so the nixpkgs JDK 25 is used here. Revisit
|
||||
# when bumping the penpot input.
|
||||
# Binaries the backend shells out to at runtime (image/font processing),
|
||||
# baked into the wrapper's PATH.
|
||||
# util-linux: provides prlimit; upstream runs font-processing subprocesses
|
||||
# under it (app.media.local/exec-font!), see also app.util.shell/prlimit-cmd.
|
||||
runtimeBinPath = lib.makeBinPath [
|
||||
imagemagick
|
||||
fontforge
|
||||
woff2
|
||||
woff-tools
|
||||
fontconfig
|
||||
util-linux
|
||||
];
|
||||
|
||||
# Offline Clojure dependencies: eval-time cache from deps-lock.json
|
||||
# (no fixed-output derivation) + fake-git shim + short-SHA expansion.
|
||||
cljOffline = callPackage ./clj-offline.nix { };
|
||||
|
||||
builtinTemplates = stdenv.mkDerivation {
|
||||
name = "penpot-backend-builtin-templates";
|
||||
src = "${src}/backend";
|
||||
|
||||
nativeBuildInputs = [
|
||||
babashka
|
||||
];
|
||||
|
||||
outputHashAlgo = "sha256";
|
||||
outputHashMode = "recursive";
|
||||
# The file contents come from the pinned penpot-files flake input, so
|
||||
# this hash only changes when that input (or the template list in
|
||||
# onboarding.edn) changes. On a penpot input bump: run
|
||||
# `nix flake update penpot-files` first (a missing file fails the build
|
||||
# loudly), then set to lib.fakeHash, build once, copy the `got:` hash
|
||||
# from the failure message.
|
||||
outputHash = "sha256-b03i34xCfKHie5AjT7is3o9kqIzcSU/nPl7mV1gu+u0=";
|
||||
dontFixup = true;
|
||||
|
||||
dontBuild = true;
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
export HOME="$TMPDIR/home"
|
||||
mkdir -p "$HOME"
|
||||
|
||||
# Map onboarding.edn (id, file-uri) pairs to files from the pinned
|
||||
# penpot-files input. EDN-native parsing via babashka; basenames are
|
||||
# URL-decoded (upstream file-uris contain %20 etc.).
|
||||
cat > "$TMPDIR/map-templates.clj" <<'EOF'
|
||||
(require '[babashka.fs :as fs]
|
||||
'[clojure.edn :as edn]
|
||||
'[clojure.string :as str])
|
||||
(import '[java.net URLDecoder])
|
||||
|
||||
(let [[defs-path files-dir dest] *command-line-args*
|
||||
data (edn/read-string (slurp defs-path))]
|
||||
(fs/create-dirs dest)
|
||||
(doseq [{:keys [id file-uri]} data]
|
||||
;; Basename of file-uri, minus any query/fragment; literal `+`
|
||||
;; is protected before URL-decoding (`URLDecoder` maps `+` to
|
||||
;; space, but upstream names use `%20` for spaces).
|
||||
(let [fname (-> file-uri
|
||||
(str/split #"/") last
|
||||
(str/split #"[?#]") first
|
||||
(str/replace "+" "%2B")
|
||||
(URLDecoder/decode "UTF-8"))
|
||||
src-file (fs/file files-dir fname)]
|
||||
(when-not (fs/exists? src-file)
|
||||
(println (format "template file %s (id: %s) not found" fname id))
|
||||
(System/exit 1))
|
||||
(println (format "=> installing %s" id))
|
||||
(fs/copy src-file (fs/file dest id)))))
|
||||
EOF
|
||||
|
||||
bb "$TMPDIR/map-templates.clj" \
|
||||
resources/app/onboarding.edn \
|
||||
"${penpot-files}" \
|
||||
"$out/templates"
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
};
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
pname = "penpot-backend";
|
||||
inherit version src;
|
||||
|
||||
nativeBuildInputs = [
|
||||
clojureJdk25
|
||||
cljOffline.fake-git
|
||||
cljOffline.clj-builder
|
||||
makeWrapper
|
||||
# Repacks the jar deterministically during fixup. Replaces
|
||||
# stripJavaArchivesHook, whose O(n^2) member reorder needs ~32 minutes on
|
||||
# this jar (54000 entries); see jarNormalizer above.
|
||||
python3
|
||||
];
|
||||
|
||||
configurePhase = ''
|
||||
runHook preConfigure
|
||||
|
||||
# Offline dependency resolution from the eval-time clj-nix cache.
|
||||
${cljOffline.setup}
|
||||
|
||||
runHook postConfigure
|
||||
'';
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
|
||||
(
|
||||
cd backend
|
||||
mkdir -p target/classes
|
||||
echo "$version" > target/classes/version.txt
|
||||
cp ../CHANGES.md target/classes/changelog.md
|
||||
clojure -T:build jar
|
||||
)
|
||||
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
installPhase = /* sh */ ''
|
||||
runHook preInstall
|
||||
|
||||
share="$out/share/penpot-backend"
|
||||
mkdir -p "$share" "$out/bin"
|
||||
|
||||
install -Dm644 backend/target/penpot.jar "$share/penpot.jar"
|
||||
install -Dm644 backend/resources/log4j2.xml "$share/log4j2.xml"
|
||||
cp -r "${builtinTemplates}/templates" "$share/builtin-templates"
|
||||
echo "$version" > "$share/version.txt"
|
||||
|
||||
# Deterministic repack (see jarNormalizer). The uber task stamps
|
||||
# entries with wall-clock time, so this can't be skipped.
|
||||
python3 ${jarNormalizer} "$share/penpot.jar"
|
||||
|
||||
# manage.py hardcodes the prepl endpoint as the argparse default; patch
|
||||
# it to honor the PREPL_URI environment variable instead.
|
||||
install -Dm755 backend/scripts/manage.py "$share/manage.py"
|
||||
substituteInPlace "$share/manage.py" \
|
||||
--replace-fail 'import argparse' 'import argparse
|
||||
import os' \
|
||||
--replace-fail 'default="tcp://localhost:6063"' 'default=os.environ.get("PREPL_URI", "tcp://localhost:6063")'
|
||||
|
||||
# Flags mirror backend/scripts/run.template.sh, except
|
||||
# --add-opens=java.base/java.nio (not upstream; needed by
|
||||
# yetti/lettuce for direct NIO buffer access on modern JDKs).
|
||||
# $JAVA_OPTS and $PENPOT_ENTRYPOINT are embedded verbatim by
|
||||
# makeWrapper and expand at runtime, like in the upstream run.sh.
|
||||
makeWrapper "${jdk25_headless}/bin/java" "$out/bin/penpot-backend" \
|
||||
--chdir "$share" \
|
||||
--prefix PATH : "${runtimeBinPath}" \
|
||||
--set MAGICK_CONFIGURE_PATH "${magickPolicy}/etc/ImageMagick-7" \
|
||||
--set-default PENPOT_ENTRYPOINT app.main \
|
||||
--add-flags '-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager' \
|
||||
--add-flags '-Dlog4j2.configurationFile=log4j2.xml' \
|
||||
--add-flags '-XX:-OmitStackTraceInFastThrow' \
|
||||
--add-flags '--sun-misc-unsafe-memory-access=allow' \
|
||||
--add-flags '--enable-native-access=ALL-UNNAMED' \
|
||||
--add-flags '--add-opens=java.base/java.nio=ALL-UNNAMED' \
|
||||
--add-flags '--enable-preview' \
|
||||
--add-flags '$JAVA_OPTS' \
|
||||
--add-flags '-jar penpot.jar -m $PENPOT_ENTRYPOINT'
|
||||
|
||||
makeWrapper "${pythonEnv}/bin/python" "$out/bin/penpot-manage" \
|
||||
--add-flags "$share/manage.py"
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
passthru = {
|
||||
inherit builtinTemplates;
|
||||
version = version;
|
||||
};
|
||||
|
||||
meta = {
|
||||
description = "Penpot backend API server (Clojure uberjar)";
|
||||
homepage = "https://penpot.app";
|
||||
license = lib.licenses.mpl20;
|
||||
platforms = lib.platforms.linux;
|
||||
mainProgram = "penpot-backend";
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user