From 206f079d5e1e93b9db4ef372fef5102d574f66e6 Mon Sep 17 00:00:00 2001 From: Ben Ford Date: Sun, 9 Aug 2026 06:25:44 +0000 Subject: [PATCH] build(nix): add flake exposing anydoc as the default package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds Nix flake support so anydoc can be consumed reproducibly without a Cargo or npm toolchain on the host. Two packages, both driving the same local (offline) Rust conversion: - `default` / `anydoc` — the bare Rust CLI, built from the `convert` example over the library. A single binary with no runtime deps, ideal for images/VMs that just need file-in / Markdown-out. - `anydoc-node` — the upstream `cli.js` over the napi addon, wrapped with a Node runtime for the fuller UX (`--help`, `--version`, stdin `-`, richer format detection). The loader is pointed at the freshly built cdylib via `NAPI_RS_NATIVE_LIBRARY_PATH`, so there are no prebuilt `.node` downloads or platform-triple filenames involved. Also exposes a devShell (cargo/rustc/rustfmt/clippy) and a nixpkgs-fmt formatter. No changes to the Rust sources; `doCheck` is disabled because the snapshot suite needs the gitignored `samples/` corpus. Co-Authored-By: Claude Opus 4.8 (1M context) --- flake.lock | 27 +++++++++++++ flake.nix | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..379045c --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1786106723, + "narHash": "sha256-zDSUbpoeo/9ZmD2+wXnzxoo1+uhL8vxc0b8yuYMKYq0=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "f13ff45afd1bb73e640eaa08a7066dbed07e3238", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..0a4a513 --- /dev/null +++ b/flake.nix @@ -0,0 +1,110 @@ +{ + description = "anydoc — convert documents (docx, pptx, xlsx, odt, rtf, epub, pdf, csv) to GitHub-Flavored Markdown, fully offline"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + + outputs = { self, nixpkgs, ... }: + let + systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; + forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system}); + version = (nixpkgs.lib.importTOML ./Cargo.toml).package.version; + in { + packages = forAllSystems (pkgs: rec { + # Bare Rust CLI: the `convert` example over the local library, no runtime + # deps. This is the direct path — a single static-ish binary for scape VMs + # and ops workflows that just need file-in / markdown-out. + anydoc = pkgs.rustPlatform.buildRustPackage { + pname = "anydoc"; + inherit version; + + src = ./.; + + cargoLock.lockFile = ./Cargo.lock; + + # The crate ships as a library plus napi/pyo3/wasm bindings; the runnable + # CLI is the `convert` example, a thin arg-parser over the public + # `to_markdown_bytes`/`to_document` functions. Build just the root crate + # and that example — never the binding members, which need node/python/wasm + # toolchains that aren't present here. + cargoBuildFlags = [ "--package" "anydoc" "--example" "convert" ]; + + # Snapshot tests depend on a gitignored `samples/` corpus that isn't in + # the source tree, so the default `cargo test` can't run here. + doCheck = false; + + # buildRustPackage only installs top-level `target/*/release` binaries; + # the example lands under `examples/`, so place it by hand as `anydoc`. + postInstall = '' + install -Dm755 \ + "$(find target -type f -name convert -path '*/release/examples/*' | head -n1)" \ + "$out/bin/anydoc" + ''; + + meta = with pkgs.lib; { + description = "Convert documents to GitHub-Flavored Markdown, fully offline"; + homepage = "https://github.com/getmissionctrl/anydoc"; + license = licenses.mit; + mainProgram = "anydoc"; + platforms = systems; + }; + }; + + # Node CLI: the same local Rust conversion behind the napi binding, driven + # by the upstream `cli.js` for the nicer experience (--help, --version, + # stdin `-`, richer format detection). Pulls in a node runtime, so it's the + # opt-in package for consumers who want the polish over the bare exe. + anydoc-node = pkgs.rustPlatform.buildRustPackage { + pname = "anydoc-node"; + inherit version; + + src = ./.; + + cargoLock.lockFile = ./Cargo.lock; + + nativeBuildInputs = [ pkgs.makeWrapper ]; + + # Build only the napi cdylib member — not the root bin or the other + # bindings. The resulting shared object is loaded by index.js. + cargoBuildFlags = [ "--package" "anydoc-node" ]; + + doCheck = false; + + # Assemble the JS package next to the freshly built native addon and + # point the loader straight at it via NAPI_RS_NATIVE_LIBRARY_PATH + # (index.js honours that env var first), sidestepping the platform-triple + # filename dance. The wrapper is the `anydoc` command. + postInstall = '' + libdir="$out/libexec/anydoc-node" + mkdir -p "$libdir" + cp node/index.js node/index.d.ts node/cli.js node/package.json "$libdir/" + cp "$(find target -type f \( -name 'libanydoc_node.so' -o -name 'libanydoc_node.dylib' \) -path '*/release/*' | head -n1)" \ + "$libdir/anydoc.node" + + makeWrapper ${pkgs.lib.getExe pkgs.nodejs} "$out/bin/anydoc" \ + --add-flags "$libdir/cli.js" \ + --set NAPI_RS_NATIVE_LIBRARY_PATH "$libdir/anydoc.node" + ''; + + meta = with pkgs.lib; { + description = "anydoc Node CLI — document-to-Markdown with --help/--version/stdin, fully offline"; + homepage = "https://github.com/getmissionctrl/anydoc"; + license = licenses.mit; + mainProgram = "anydoc"; + platforms = systems; + }; + }; + + default = anydoc; + }); + + devShells = forAllSystems (pkgs: { + default = pkgs.mkShell { + packages = with pkgs; [ cargo rustc rustfmt clippy ]; + }; + }); + + formatter = forAllSystems (pkgs: pkgs.nixpkgs-fmt); + }; +}