mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-24 02:06:54 -06:00
Add a Nix flake (NixOS, GNOME and Hyprland)
Builds the desktop app and the CLI from source with `nix build`, and offers packages, an overlay, `nix run`, a dev shell and a formatting check. - Cargo.lock drives the dependencies (git sources via allowBuiltinFetchGit). - Installs the desktop file, hicolor icons, MIME types and AppStream metadata from packaging/linux/, validated at build time. - Wraps the binaries so the dlopen'd libraries (libwayland-client, libxkbcommon(-x11), X11, Vulkan loader, libEGL, libdbus for the portal file chooser) are found on NixOS: native Wayland on GNOME and Hyprland, X11 as fallback. - Fonts from storytold/craft-fonts are pinned to the release workflow's revision where the app embeds them. - doCheck is off (the suite needs corpora and a GPU); `nix build` was verified. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
d020b4e85d
commit
947b513163
Generated
+45
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"craft-fonts": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1791295400,
|
||||||
|
"narHash": "sha256-e+6HpOYoAFTh6mNfGBBw2njnBzcE8W8+Jybv1+dKOCA=",
|
||||||
|
"owner": "storytold",
|
||||||
|
"repo": "craft-fonts",
|
||||||
|
"rev": "abb83316d96aa59c1cf64784289e378fe9fa5695",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "storytold",
|
||||||
|
"repo": "craft-fonts",
|
||||||
|
"rev": "abb83316d96aa59c1cf64784289e378fe9fa5695",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1791594108,
|
||||||
|
"narHash": "sha256-XK4KceonkwD8LuMAt/vNrz982mXJM5jeHAxMwsUXXAQ=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "6510408d147d8c5e6d76043e899e9251756f6a75",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"craft-fonts": "craft-fonts",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
{
|
||||||
|
description = "DesignCraft — page layout and publishing, a free alternative to InDesign, in pure Rust";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
# DesignCraft needs edition 2024 and rustc ≥ 1.95, so the flake tracks unstable. Consumers on a
|
||||||
|
# release channel can retarget it: `inputs.designcraft.inputs.nixpkgs.follows = "nixpkgs";`
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
|
||||||
|
# Optional build input of the repository: the fonts that the build embeds when `CRAFT_FONTS_DIR`
|
||||||
|
# is set. Official releases always build with them; without them everything works but some
|
||||||
|
# scripts have no glyphs. Pinned to the revision the release workflow uses — bump deliberately
|
||||||
|
# (`nix flake update craft-fonts`).
|
||||||
|
craft-fonts = {
|
||||||
|
url = "github:storytold/craft-fonts/abb83316d96aa59c1cf64784289e378fe9fa5695";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
craft-fonts,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
inherit (nixpkgs) lib;
|
||||||
|
|
||||||
|
systems = [
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
"aarch64-darwin"
|
||||||
|
];
|
||||||
|
|
||||||
|
# The AppStream metadata carries a release date. The flake source's own commit date is
|
||||||
|
# reproducible, unlike "now"; a tarball or dirty tree without one falls back to the epoch.
|
||||||
|
buildDate =
|
||||||
|
let
|
||||||
|
d = self.lastModifiedDate or "";
|
||||||
|
in
|
||||||
|
if builtins.stringLength d >= 8 then
|
||||||
|
"${builtins.substring 0 4 d}-${builtins.substring 4 2 d}-${builtins.substring 6 2 d}"
|
||||||
|
else
|
||||||
|
"1970-01-01";
|
||||||
|
|
||||||
|
packageArgs = {
|
||||||
|
inherit craft-fonts buildDate;
|
||||||
|
};
|
||||||
|
|
||||||
|
forAllSystems = f: lib.genAttrs systems (system: f (import nixpkgs { inherit system; }) system);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages = forAllSystems (
|
||||||
|
pkgs: _:
|
||||||
|
let
|
||||||
|
designcraft = pkgs.callPackage ./nix/package.nix packageArgs;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit designcraft;
|
||||||
|
default = designcraft;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
# `nixpkgs.overlays = [ inputs.designcraft.overlays.default ]` → `pkgs.designcraft` everywhere.
|
||||||
|
overlays.default = final: _prev: {
|
||||||
|
designcraft = final.callPackage ./nix/package.nix packageArgs;
|
||||||
|
};
|
||||||
|
|
||||||
|
apps = forAllSystems (
|
||||||
|
_: system: {
|
||||||
|
default = {
|
||||||
|
type = "app";
|
||||||
|
program = lib.getExe self.packages.${system}.designcraft;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
devShells = forAllSystems (
|
||||||
|
pkgs: system:
|
||||||
|
let
|
||||||
|
designcraft = self.packages.${system}.designcraft;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
default = pkgs.mkShell (
|
||||||
|
{
|
||||||
|
# The package's own native inputs, so `cargo build` in the shell needs no extra setup.
|
||||||
|
inputsFrom = [ designcraft ];
|
||||||
|
packages = with pkgs; [
|
||||||
|
cargo
|
||||||
|
rustc
|
||||||
|
clippy
|
||||||
|
rustfmt
|
||||||
|
rust-analyzer
|
||||||
|
];
|
||||||
|
# `cargo run` dlopens the Vulkan loader, libwayland and libxkbcommon like the packaged binary.
|
||||||
|
LD_LIBRARY_PATH = lib.makeLibraryPath designcraft.passthru.runtimeLibs;
|
||||||
|
RUST_BACKTRACE = "1";
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs (craft-fonts != null) {
|
||||||
|
# Embedded fonts in `cargo run` too; build.rs reads this as CRAFT_FONTS_DIR.
|
||||||
|
CRAFT_FONTS_DIR = toString craft-fonts;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
checks = forAllSystems (
|
||||||
|
pkgs: system: {
|
||||||
|
designcraft = self.packages.${system}.designcraft;
|
||||||
|
|
||||||
|
# `nix fmt` formats the flake; this keeps it formatted.
|
||||||
|
formatting =
|
||||||
|
pkgs.runCommand "designcraft-nix-formatting" { nativeBuildInputs = [ pkgs.nixfmt ]; }
|
||||||
|
''
|
||||||
|
nixfmt --check ${./flake.nix} ${./nix}
|
||||||
|
touch $out
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
formatter = forAllSystems (pkgs: _: pkgs.nixfmt);
|
||||||
|
};
|
||||||
|
}
|
||||||
+213
@@ -0,0 +1,213 @@
|
|||||||
|
# DesignCraft — the desktop app (`designcraft`) and the CLI/MCP server (`designcraft-cli`), built from this
|
||||||
|
# repository:
|
||||||
|
#
|
||||||
|
# nix build # → ./result/bin/designcraft, ./result/bin/designcraft-cli
|
||||||
|
#
|
||||||
|
# Kept in step with the release workflow (.github/workflows/release.yml) and packaging/:
|
||||||
|
# * `Cargo.lock` drives the dependencies (`cargoLock`), so there is no vendorHash to bump. The
|
||||||
|
# git sources (storytold/craft-libs) are fetched at the revision the lock file pins;
|
||||||
|
# * the desktop file, hicolor icons, MIME types and AppStream metadata are the same files the
|
||||||
|
# .deb/.rpm ship (packaging/linux/), so `apt` and NixOS users see one identical DesignCraft;
|
||||||
|
# * `CRAFT_FONTS_DIR` embeds the fonts from storytold/craft-fonts (the `craft-fonts` flake input, pinned to
|
||||||
|
# the revision the release workflow uses); without it everything builds and runs, but some
|
||||||
|
# scripts (e.g. Japanese, Chinese) have no glyphs;
|
||||||
|
# * `doCheck` is off: the test suite needs corpora and a GPU the Nix sandbox cannot provide
|
||||||
|
# (`cargo xtask ci` is the gate). `pkgs.designcraft.overrideAttrs { doCheck = true; }` runs it.
|
||||||
|
#
|
||||||
|
# Everything in the product is pure Rust, so this needs no build system beyond cargo plus the
|
||||||
|
# libraries winit/wgpu `dlopen` at run time (see `runtimeLibs`).
|
||||||
|
#
|
||||||
|
# Desktop sessions: the window opens natively on Wayland (GNOME/Mutter, Hyprland and other
|
||||||
|
# wlroots-style compositors) and falls back to X11/XWayland. File dialogs go through
|
||||||
|
# xdg-desktop-portal, so the session needs a portal backend: GNOME ships one
|
||||||
|
# (`xdg-desktop-portal-gnome`); on Hyprland enable
|
||||||
|
# xdg.portal = { enable = true; extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; };
|
||||||
|
# next to `programs.hyprland.enable = true;`.
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
stdenv,
|
||||||
|
rustPlatform,
|
||||||
|
pkg-config,
|
||||||
|
makeWrapper,
|
||||||
|
# Windowing/GPU libraries: needed at build time by the crates that link them, at run time by the
|
||||||
|
# ones that open them with dlopen() (see `runtimeLibs`), and by the dev shell (flake.nix).
|
||||||
|
wayland,
|
||||||
|
wayland-scanner,
|
||||||
|
libxkbcommon,
|
||||||
|
libx11,
|
||||||
|
libxcb,
|
||||||
|
libxcursor,
|
||||||
|
libxi,
|
||||||
|
libxrandr,
|
||||||
|
vulkan-loader,
|
||||||
|
libglvnd,
|
||||||
|
# rfd opens libdbus-1 at run time to reach the xdg-desktop-portal file chooser (Open/Save dialogs).
|
||||||
|
dbus,
|
||||||
|
# Validates the desktop file and the AppStream metadata we install (as CI does).
|
||||||
|
desktop-file-utils,
|
||||||
|
appstream,
|
||||||
|
# Adds the driver search path (/run/opengl-driver/lib) to the binaries' RPATH so wgpu finds the
|
||||||
|
# installed Vulkan/GL driver on NixOS. Optional: harmless on nixpkgs without it.
|
||||||
|
autoAddDriverRunpath ? null,
|
||||||
|
# storytold/craft-fonts checkout (flake input). `null` builds without the embedded fonts.
|
||||||
|
craft-fonts ? null,
|
||||||
|
# Release date for the AppStream metadata (YYYY-MM-DD); the flake derives it from its own commit.
|
||||||
|
buildDate ? "1970-01-01",
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
root = ../.;
|
||||||
|
|
||||||
|
# The version lives in exactly one place: `[workspace.package] version` in the root Cargo.toml.
|
||||||
|
version = (builtins.fromTOML (builtins.readFile (root + "/Cargo.toml"))).workspace.package.version;
|
||||||
|
|
||||||
|
isLinux = stdenv.hostPlatform.isLinux;
|
||||||
|
|
||||||
|
# AppStream id: also the desktop file name, and the icon name in share/icons/hicolor.
|
||||||
|
appId = "ai.storyteller.designcraft";
|
||||||
|
|
||||||
|
# Libraries the binaries open at run time with dlopen(): nothing links them, so no RPATH points at
|
||||||
|
# them. winit loads libwayland-client, libxkbcommon(-x11) and the X11 libraries; wgpu loads the
|
||||||
|
# Vulkan loader or libEGL (libglvnd); rfd loads libdbus-1 for the file chooser portal. NixOS patches the loader to find the drivers in
|
||||||
|
# /run/opengl-driver. The wrapper below points LD_LIBRARY_PATH here.
|
||||||
|
runtimeLibs = lib.optionals isLinux [
|
||||||
|
libxkbcommon
|
||||||
|
wayland
|
||||||
|
libx11
|
||||||
|
libxcb
|
||||||
|
libxcursor
|
||||||
|
libxi
|
||||||
|
libxrandr
|
||||||
|
vulkan-loader
|
||||||
|
libglvnd
|
||||||
|
dbus
|
||||||
|
];
|
||||||
|
|
||||||
|
# The two native binaries. The wasm app (apps/designcraft-web) is built by `cargo xtask`, not here.
|
||||||
|
binaries = [
|
||||||
|
"designcraft"
|
||||||
|
"designcraft-cli"
|
||||||
|
];
|
||||||
|
|
||||||
|
in
|
||||||
|
rustPlatform.buildRustPackage {
|
||||||
|
pname = "designcraft";
|
||||||
|
inherit version;
|
||||||
|
|
||||||
|
src = lib.cleanSourceWith {
|
||||||
|
src = lib.cleanSource root;
|
||||||
|
# The Nix files are not part of the build: editing them must not rebuild any Rust.
|
||||||
|
filter =
|
||||||
|
path: _type:
|
||||||
|
!(builtins.elem (baseNameOf path) [
|
||||||
|
"flake.nix"
|
||||||
|
"flake.lock"
|
||||||
|
"nix"
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
cargoLock = {
|
||||||
|
lockFile = root + "/Cargo.lock";
|
||||||
|
# The git dependencies are pinned by revision in Cargo.lock.
|
||||||
|
allowBuiltinFetchGit = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Both binaries, as the .deb/.rpm install them (`packaging/linux/package.sh`).
|
||||||
|
cargoBuildFlags = [
|
||||||
|
"--locked"
|
||||||
|
"-p"
|
||||||
|
"designcraft"
|
||||||
|
"-p"
|
||||||
|
"designcraft-cli"
|
||||||
|
];
|
||||||
|
|
||||||
|
doCheck = false;
|
||||||
|
|
||||||
|
strictDeps = true;
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
pkg-config
|
||||||
|
]
|
||||||
|
++ lib.optionals isLinux [
|
||||||
|
makeWrapper
|
||||||
|
wayland-scanner
|
||||||
|
desktop-file-utils
|
||||||
|
appstream
|
||||||
|
]
|
||||||
|
++ lib.optionals (isLinux && autoAddDriverRunpath != null) [ autoAddDriverRunpath ];
|
||||||
|
|
||||||
|
buildInputs = lib.optionals isLinux [
|
||||||
|
wayland
|
||||||
|
libxkbcommon
|
||||||
|
libx11
|
||||||
|
libxcb
|
||||||
|
libxcursor
|
||||||
|
libxi
|
||||||
|
libxrandr
|
||||||
|
];
|
||||||
|
|
||||||
|
env = lib.optionalAttrs (craft-fonts != null) {
|
||||||
|
CRAFT_FONTS_DIR = toString craft-fonts;
|
||||||
|
# A malformed font input must fail the build rather than silently ship without glyphs.
|
||||||
|
CRAFT_FONTS_REQUIRED = "1";
|
||||||
|
};
|
||||||
|
|
||||||
|
postInstall = ''
|
||||||
|
# Desktop integration: byte-for-byte the files the .deb/.rpm/.AppImage install.
|
||||||
|
install -Dm644 packaging/linux/${appId}.desktop $out/share/applications/${appId}.desktop
|
||||||
|
install -Dm644 packaging/linux/${appId}.mime.xml $out/share/mime/packages/${appId}.xml
|
||||||
|
cp -r assets/app-icon/hicolor $out/share/icons/
|
||||||
|
install -d $out/share/metainfo
|
||||||
|
substitute packaging/linux/${appId}.metainfo.xml.in \
|
||||||
|
$out/share/metainfo/${appId}.metainfo.xml \
|
||||||
|
--subst-var-by VERSION ${version} \
|
||||||
|
--subst-var-by DATE ${buildDate}
|
||||||
|
|
||||||
|
# Licences for everything embedded in the binaries.
|
||||||
|
doc=$out/share/doc/designcraft
|
||||||
|
for f in README.md LICENSE-MIT LICENSE-APACHE NOTICE ATTRIBUTION.md assets/ATTRIBUTION.md; do
|
||||||
|
if [ -f "$f" ]; then install -Dm644 "$f" "$doc/$(basename "$f")"; fi
|
||||||
|
done
|
||||||
|
for f in assets/fonts/OFL-*.txt; do
|
||||||
|
if [ -f "$f" ]; then install -Dm644 "$f" "$doc/$(basename "$f")"; fi
|
||||||
|
done
|
||||||
|
''
|
||||||
|
+ lib.optionalString (craft-fonts != null) ''
|
||||||
|
for lic in ${toString craft-fonts}/fonts/*/OFL.txt; do
|
||||||
|
install -Dm644 "$lic" "$doc/OFL-$(basename "$(dirname "$lic")").txt"
|
||||||
|
done
|
||||||
|
''
|
||||||
|
+ lib.optionalString isLinux ''
|
||||||
|
desktop-file-validate $out/share/applications/${appId}.desktop
|
||||||
|
XDG_CACHE_HOME=$TMPDIR/.cache appstreamcli validate --no-net \
|
||||||
|
$out/share/metainfo/${appId}.metainfo.xml
|
||||||
|
|
||||||
|
# makeWrapper keeps the binaries working when the libs above are only dlopen'd.
|
||||||
|
for bin in ${lib.concatStringsSep " " (map (b: "$out/bin/" + b) binaries)}; do
|
||||||
|
wrapProgram "$bin" --prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath runtimeLibs}"
|
||||||
|
done
|
||||||
|
'';
|
||||||
|
|
||||||
|
passthru = {
|
||||||
|
# Shared with the dev shell so `cargo run` inside `nix develop` finds the same dlopen'd libs.
|
||||||
|
inherit runtimeLibs;
|
||||||
|
};
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "Page layout and publishing application";
|
||||||
|
longDescription = ''
|
||||||
|
DesignCraft is a page layout and publishing application: multi-page documents, styles,
|
||||||
|
masters, typography and print output. It ships with designcraft-cli, a headless command runner
|
||||||
|
and MCP server for AI agents.
|
||||||
|
'';
|
||||||
|
homepage = "https://getartcraft.com/apps/designcraft";
|
||||||
|
license = with lib.licenses; [
|
||||||
|
mit
|
||||||
|
asl20
|
||||||
|
];
|
||||||
|
sourceProvenance = with lib.sourceTypes; [ fromSource ];
|
||||||
|
platforms = lib.platforms.linux ++ lib.platforms.darwin;
|
||||||
|
# `nix run` and the desktop entry both start the GUI.
|
||||||
|
mainProgram = "designcraft";
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user