readest/flake.nix
dastarruer d4bf089ac2
feat: package for nix (#5605)
* refactor(nix): move nix files out of `ops/`

* feat(nix): package for nix

* docs(nix): add installation guide

* chore(workflows): add workflow to build on nix

* chore(workflows): move flake check to pull requests, and builds to cron

* fix(nix): only add ios shell on darwin

* chore(workflows): add workflow to update flake inputs periodically

* chore(workflows): rename nix.yml to nix-build.yml

* chore(workflows): add cachix to nix-build

* chore: check all systems

* chore: auto-update hashes

* fix: set platform to linux and add warning in docs

* fix: only evaluate android shell if system is not aarch64

* fix: drop soon-to-be-deprecated x86_64-darwin platform

* fix: use more explicit nix build command

* fix: respect .gitignore in src

* chore: only run nix build on push to main

* fix: remove duplicate nix installer action

* chore: fix script formatting

* chore: move build check to flake checks

* chore: remove automatic hash-mismatch fix

* fix: only give read permissions to nix build action

* fix: use more explicit matching

* fix: remove unnecessary passthru references

* fix: pin action versions

* fix: use cachix for nix flake check

* fix: remove unnecessary secret

* fix: do not push flake check builds

* fix: meta.platforms -> platforms

* fix(nix): wire up the public binary cache and drop aarch64-linux

The Cachix cache was populated by CI but unreachable by users: neither the
flake nor the README pointed at it, so every install compiled the Rust/Tauri
stack and webkitgtk from source. Add nixConfig substituters and document the
cachix use / nix.settings opt-ins.

skipPush was passed to install-nix-action, which has no such input, so PR
builds still pushed whenever the token was present. Move it to cachix-action
and drop authToken there: the cache is public, so pulling needs no token, and
PR-triggered builds no longer see the push token at all.

Restrict nix_flake_check to pull_request. It also ran on main pushes, where
nix-build.yml already builds and pushes the same paths.

Drop aarch64-linux from eachSystem. Nothing built or cached it, so ARM Linux
users got a cache miss on everything. That makes the two system != aarch64-linux
guards dead, so fold those attrsets back into their bases.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(nix): refresh the pnpm deps hash after lockfile changes on main

nix flake check builds the pull_request merge ref, so the fixed-output
derivation covers main's pnpm-lock.yaml, not just the branch's. Two lockfile
changes landed on main after the hash was pinned (#5754, #5764), so the FOD
went stale and the check failed with a hash mismatch.

Cargo.lock and the turso plugin are unchanged over the same range, so
cargoHash and tursoPluginDeps still match.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* perf(nix): make the PR flake check evaluation only

nix flake check builds every attribute in `checks`, so `checks.build =
packages.default` turned the PR check into a full release build: cargo vendor,
a production Next.js build, and a cold release compile of the whole Tauri tree.
Nix builds in a pure sandbox, so it cannot use the sccache and rust-cache the
other Rust jobs rely on, and the binary cache is still empty, so nothing
amortized it. Observed over 45 minutes per run.

Drop the attribute. The flake outputs are still evaluated across systems, which
catches eval errors, and nix-build.yml keeps doing the real build on main and
pushing the result to the cache.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore(nix): pin actions/checkout to a commit sha

nix-build.yml used the mutable v5 tag while the other 19 checkout usages in
this repo pin 3d3c42e5aac5ba805825da76410c181273ba90b1 (v7.0.1). A movable tag
lets the action change under us and is what the scorecard pinned-dependencies
check flags.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(nix): compress the README install and fix the dev shell commands

README: five code blocks down to three. The three interchangeable ways to opt
in to the cache collapse to one, and the two configuration.nix snippets merge
into a single block, since a NixOS user edits one file. Note why the cache has
to be repeated there: a flake input's nixConfig does not apply to the importing
system build, so the systemPackages path would otherwise build from source.

CONTRIBUTING: the documented shells pointed at ./ops, which this branch
deleted when it moved the flake to the repository root, so all three commands
failed. The web shell is also named default now, and ios is gated behind
isDarwin.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 18:03:22 +02:00

220 lines
7.2 KiB
Nix

{
description = "Readest development environment";
# Points `nix run`/`nix build` at the project's public Cachix cache so users
# do not compile the Rust/Tauri stack and webkitgtk from source. Nix ignores
# this for non-trusted users and otherwise prompts for consent, so the README
# also documents `cachix use readest` as the permanent opt-in.
nixConfig = {
extra-substituters = [ "https://readest.cachix.org" ];
extra-trusted-public-keys = [
"readest.cachix.org-1:KvKAePcZZCZB8ytFIAOGdgN3VRdmFHGRMHqMVckbt5c="
];
};
inputs = {
self.submodules = true;
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
android = {
url = "github:tadfisher/android-nixpkgs/stable";
};
fenix = {
url = "github:nix-community/fenix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, flake-utils, android, fenix }:
flake-utils.lib.eachSystem [
"x86_64-linux"
"aarch64-darwin"
]
(system:
let
inherit (nixpkgs) lib;
inherit (pkgs.lib) optionals;
inherit (pkgs.stdenv) isDarwin;
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
overlays = [ fenix.overlays.default ];
};
toolchain = with pkgs.fenix.complete; [
cargo
clippy
rust-src
rustc
rustfmt
];
commonNativeBuildInupts = with pkgs; [
pnpm
nodejs_24
clang
rust-analyzer-nightly
pkg-config
xdg-utils
patchelf
wrapGAppsHook4
playwright-driver.browsers
self.formatter.${pkgs.stdenv.hostPlatform.system}
];
commonBuildInputs = with pkgs; [
at-spi2-atk
atkmm
cairo
fontconfig
freetype
gdk-pixbuf
glib
gtk3
harfbuzz
librsvg
libsoup_3
openssl
pango
zlib
gst_all_1.gstreamer
gst_all_1.gst-plugins-base
gst_all_1.gst-plugins-good
gst_all_1.gst-plugins-bad
] ++ (optionals (!isDarwin) [
webkitgtk_4_1
]) ++ (optionals isDarwin [
darwin.libiconv
]);
mkCommonShell =
{ name
, postInit ? ""
, extraNativeBuildInputs ? [ ]
, extraTargets ? [ ]
, extraEnv ? { }
}:
pkgs.mkShell rec {
inherit name;
nativeBuildInputs = commonNativeBuildInupts ++ extraNativeBuildInputs;
buildInputs = commonBuildInputs ++ [
(
with pkgs.fenix;
combine [
toolchain
extraTargets
]
)
];
env = {
GDK_BACKEND = "x11";
LD_LIBRARY_PATH = lib.makeLibraryPath buildInputs;
PLAYWRIGHT_BROWSERS_PATH = pkgs.playwright-driver.browsers;
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = 1;
PLAYWRIGHT_HOST_PLATFORM_OVERRIDE = "ubuntu-24.04";
} // extraEnv;
shellHook = ''
git submodule update --init --recursive
pnpm install
${postInit}
'';
};
in
{
packages = {
android-sdk = android.sdk.${system} (sdkPkgs: with sdkPkgs; [
build-tools-36-0-0
build-tools-35-0-0
build-tools-34-0-0
cmdline-tools-latest
emulator
platform-tools
platforms-android-36
platforms-android-35
platforms-android-34
]
++ lib.optionals (system == "aarch64-darwin") [
system-images-android-34-google-apis-arm64-v8a
system-images-android-34-google-apis-playstore-arm64-v8a
]
++ lib.optionals (system == "x86_64-linux") [
system-images-android-34-google-apis-x86-64
system-images-android-34-google-apis-playstore-x86-64
]);
} // lib.optionalAttrs (!isDarwin) {
default = pkgs.callPackage ./nix/package.nix { };
};
devShells = {
default = mkCommonShell {
name = "readest-dev";
};
android =
let
android-sdk = self.packages.${system}.android-sdk;
in
mkCommonShell
rec {
name = "readest-android";
postInit = ''
rm -rf apps/readest-app/src-tauri/gen/android
pnpm tauri android init
git checkout apps/readest-app/src-tauri/gen/android
pnpm tauri icon ../../data/icons/readest-book.png
if [ ! -d "$ANDROID_AVD_HOME/${name}.avd" ]; then
avdmanager create avd \
-n ${name} \
-k "system-images;android-34;google_apis;x86_64" \
-d "pixel" \
--force
fi
'';
extraTargets = with pkgs.fenix.targets; [
aarch64-linux-android.latest.rust-std
armv7-linux-androideabi.latest.rust-std
i686-linux-android.latest.rust-std
x86_64-linux-android.latest.rust-std
];
extraNativeBuildInputs = [
android-sdk
pkgs.gradle
pkgs.jdk
];
extraEnv = {
ANDROID_HOME = "${android-sdk}/share/android-sdk";
ANDROID_SDK_ROOT = "${android-sdk}/share/android-sdk";
NDK_HOME = "${android-sdk}/share/android-sdk/ndk/26.1.10909125";
JAVA_HOME = pkgs.jdk.home;
ANDROID_AVD_HOME = "$XDG_CONFIG_HOME/.android/avd";
};
};
} // lib.optionalAttrs isDarwin {
ios = mkCommonShell {
name = "readest-ios";
extraNativeBuildInputs = [ pkgs.cocoapods ];
};
};
formatter = pkgs.nixpkgs-fmt;
# Deliberately no `checks.build`. `nix flake check` builds everything
# in `checks`, so pointing it at packages.default turned the PR check
# into a full release build: cargo vendor, a production Next.js build,
# and a cold release compile of the whole Tauri tree, with no sccache
# or rust-cache available inside the nix sandbox. That ran ~45 min per
# PR. The flake outputs are still evaluated across systems here, which
# catches eval errors, and `nix build` in nix-build.yml does the real
# build on main and pushes the result to the binary cache.
checks = { };
});
}