diff --git a/.github/workflows/flake-test.yml b/.github/workflows/flake-test.yml new file mode 100644 index 0000000..4a0b4df --- /dev/null +++ b/.github/workflows/flake-test.yml @@ -0,0 +1,49 @@ +name: Flake Test + +on: + pull_request: + paths: + - "flake.nix" + - "flake.lock" + - ".github/workflows/flake-test.yml" + push: + branches: + - main + paths: + - "flake.nix" + - "flake.lock" + - ".github/workflows/flake-test.yml" + workflow_dispatch: + +permissions: + contents: read + +jobs: + flake: + name: validate-flake + runs-on: ubuntu-22.04 + timeout-minutes: 90 + + steps: + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #v6.0.1 + + - name: Install Nix + uses: cachix/install-nix-action@v31 + with: + extra_nix_config: | + experimental-features = nix-command flakes + + - name: Evaluate flake outputs + run: nix flake show --all-systems + + - name: Check setup app is exposed + run: nix eval .#apps.x86_64-linux.setup.program --raw + + - name: Run flake setup app + env: + CI: "true" + run: nix run .#setup + + - name: Run flake info app + run: nix run .#info diff --git a/flake.lock b/flake.lock index d1976e0..99b3532 100644 --- a/flake.lock +++ b/flake.lock @@ -37,28 +37,7 @@ "root": { "inputs": { "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs", - "rust-overlay": "rust-overlay" - } - }, - "rust-overlay": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1767926800, - "narHash": "sha256-x0n73J6ufD/EhDlVdcoAmF0OQHZ+b0a2cKDc8RZyt+o=", - "owner": "oxalica", - "repo": "rust-overlay", - "rev": "499e9eed88ff9494b6604205b42847e847dfeb91", - "type": "github" - }, - "original": { - "owner": "oxalica", - "repo": "rust-overlay", - "type": "github" + "nixpkgs": "nixpkgs" } }, "systems": { diff --git a/flake.nix b/flake.nix index 9061bf2..6893b39 100644 --- a/flake.nix +++ b/flake.nix @@ -1,66 +1,341 @@ { - description = "Donut Browser Development Environment"; + description = "Donut Browser development environment and quick-start commands"; inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; - rust-overlay = { - url = "github:oxalica/rust-overlay"; - inputs.nixpkgs.follows = "nixpkgs"; - }; }; - outputs = { self, nixpkgs, flake-utils, rust-overlay, ... }: + outputs = { self, nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system: let - overlays = [ (import rust-overlay) ]; pkgs = import nixpkgs { - inherit system overlays; + inherit system; + config.allowUnfree = true; }; + lib = pkgs.lib; - # Rust toolchain - rustToolchain = pkgs.rust-bin.stable.latest.default.override { - extensions = [ "rust-src" "rust-analyzer" "clippy" "rustfmt" ]; - }; + nodejs = + if pkgs ? nodejs_23 then + pkgs.nodejs_23 + else + pkgs.nodejs_22; - # System dependencies for Tauri on Linux - libraries = with pkgs; [ + rustPackages = with pkgs; [ + cargo + clippy + rust-analyzer + rustc + rustfmt + ]; + + commonLibs = with pkgs; [ webkitgtk_4_1 + libsoup_3 + glib gtk3 cairo gdk-pixbuf - glib + pango + atk + at-spi2-atk + at-spi2-core dbus - librsvg - libsoup_3 + nss + nspr + libdrm + libgbm + libxkbcommon + libx11 + libxcomposite + libxdamage + libxext + libxfixes + libxrandr + libxcb + libxshmfence + libxtst + libxi + xdotool + libxrender + libxinerama + libxcursor + libxscrnsaver + fontconfig + freetype + fribidi + harfbuzz + expat + libglvnd + libgpg-error + e2fsprogs + gmp + zlib + stdenv.cc.cc.lib ]; - packages = with pkgs; [ - rustToolchain - nodejs_22 - pnpm - pkg-config - cargo-tauri - openssl - # App specific tools - biome - ] ++ libraries; + runtimeLibPath = lib.makeLibraryPath commonLibs; + nixLd = pkgs.stdenv.cc.bintools.dynamicLinker; + pkgConfigLibs = [ + pkgs.at-spi2-atk + pkgs.at-spi2-core + pkgs.cairo + pkgs.dbus + pkgs.gdk-pixbuf + pkgs.glib + pkgs.gtk3 + pkgs.libsoup_3 + pkgs.libxkbcommon + pkgs.openssl + pkgs.pango + pkgs.harfbuzz + pkgs.webkitgtk_4_1 + ]; + pkgConfigPath = lib.makeSearchPath "lib/pkgconfig" ( + pkgConfigLibs ++ map lib.getDev pkgConfigLibs + ); + releaseVersion = "0.17.6"; + releaseAppImage = + if system == "x86_64-linux" then + pkgs.fetchurl { + url = "https://github.com/zhom/donutbrowser/releases/download/v${releaseVersion}/Donut_0.17.6_amd64.AppImage"; + hash = "sha256-Bmqmb0zgaC02DKC9gcyI/St9wfwFWTEoYybOb1LXiS0="; + } + else if system == "aarch64-linux" then + pkgs.fetchurl { + url = "https://github.com/zhom/donutbrowser/releases/download/v${releaseVersion}/Donut_0.17.6_aarch64.AppImage"; + hash = "sha256-FOV0PlYw59gY1QSoFrUcixtUcnFt27EYAzZE/KNQUrM="; + } + else + null; + releaseUnpacked = + if releaseAppImage != null then + pkgs.stdenvNoCC.mkDerivation { + pname = "donut-release-unpacked"; + version = releaseVersion; + src = releaseAppImage; + dontUnpack = true; + nativeBuildInputs = [ pkgs.xz ]; + installPhase = '' + runHook preInstall + cp "$src" ./donut.AppImage + chmod +x ./donut.AppImage + ./donut.AppImage --appimage-extract >/dev/null + + mkdir -p "$out" + cp -a ./squashfs-root "$out/" + + runHook postInstall + ''; + } + else + null; + releaseWrapped = + if releaseAppImage != null then + pkgs.appimageTools.wrapType2 { + pname = "donut"; + version = releaseVersion; + src = releaseAppImage; + extraPkgs = _: commonLibs; + extraInstallCommands = '' + for bin in "$out"/bin/*; do + if [ -f "$bin" ]; then + mv "$bin" "$out/bin/donut-release" + break + fi + done + ''; + } + else + null; + releaseLauncher = + if releaseUnpacked != null then + pkgs.writeShellApplication { + name = "donut-release-start"; + runtimeInputs = with pkgs; [ + coreutils + xdg-utils + ]; + text = '' + set -euo pipefail + + if [ -x "${releaseWrapped}/bin/donut-release" ]; then + if "${releaseWrapped}/bin/donut-release" "$@"; then + exit 0 + fi + echo "Wrapped AppImage failed, retrying with direct AppRun..." >&2 + fi + + export LD_LIBRARY_PATH="${releaseUnpacked}/squashfs-root/usr/lib:${releaseUnpacked}/squashfs-root/usr/lib64:${runtimeLibPath}:''${LD_LIBRARY_PATH:-}" + export NIX_LD_LIBRARY_PATH="$LD_LIBRARY_PATH" + export LIBRARY_PATH="$LD_LIBRARY_PATH" + export XDG_DATA_DIRS="${releaseUnpacked}/squashfs-root/usr/share:''${XDG_DATA_DIRS:-}" + exec "${releaseUnpacked}/squashfs-root/AppRun" "$@" + ''; + } + else + pkgs.writeShellApplication { + name = "donut-release-start"; + text = '' + echo "Release launcher is supported only on Linux (x86_64/aarch64)." + exit 1 + ''; + }; + + mkApp = name: text: + let + app = pkgs.writeShellApplication { + inherit name; + runtimeInputs = with pkgs; [ + bash + coreutils + findutils + git + gnugrep + gnused + curl + gcc + pkg-config + openssl + cargo + clippy + rustc + rustfmt + nodejs + pnpm + cargo-tauri + ]; + text = '' + export NODE_ENV=development + export NIX_LD="${nixLd}" + export NIX_LD_LIBRARY_PATH="${runtimeLibPath}:''${NIX_LD_LIBRARY_PATH:-}" + export LD_LIBRARY_PATH="${runtimeLibPath}:''${LD_LIBRARY_PATH:-}" + export LIBRARY_PATH="${runtimeLibPath}:''${LIBRARY_PATH:-}" + export PKG_CONFIG_PATH="${pkgConfigPath}:''${PKG_CONFIG_PATH:-}" + export RUST_SRC_PATH="${pkgs.rustPlatform.rustLibSrc}" + ${text} + ''; + }; + in + { + type = "app"; + program = "${app}/bin/${name}"; + }; in { devShells.default = pkgs.mkShell { - buildInputs = packages; + packages = with pkgs; [ + nodejs + pnpm + cargo-tauri + pkg-config + openssl + git + bashInteractive + gnumake + clang + llvmPackages.bintools + python3 + curl + wget + unzip + zip + xz + biome + docker + ] ++ rustPackages ++ commonLibs; shellHook = '' - export LD_LIBRARY_PATH=${pkgs.lib.makeLibraryPath libraries}:$LD_LIBRARY_PATH - export XDG_DATA_DIRS=${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}:${pkgs.gtk3}/share/gsettings-schemas/${pkgs.gtk3.name}:$XDG_DATA_DIRS - - echo "🍩 Donut Browser Dev Environment Loaded!" - echo "Node: $(node --version)" - echo "Rust: $(rustc --version)" - echo "Tauri CLI: $(cargo-tauri --version)" + export NODE_ENV=development + export NIX_LD="${nixLd}" + export NIX_LD_LIBRARY_PATH="${runtimeLibPath}:''${NIX_LD_LIBRARY_PATH:-}" + export LD_LIBRARY_PATH="${runtimeLibPath}:''${LD_LIBRARY_PATH:-}" + export LIBRARY_PATH="${runtimeLibPath}:''${LIBRARY_PATH:-}" + export PKG_CONFIG_PATH="${pkgConfigPath}:''${PKG_CONFIG_PATH:-}" + export RUST_SRC_PATH="${pkgs.rustPlatform.rustLibSrc}" + export XDG_DATA_DIRS="${pkgs.gsettings-desktop-schemas}/share:${pkgs.gtk3}/share:''${XDG_DATA_DIRS:-}" + + echo "Donut Browser dev shell ready." + echo "Quick start:" + echo " nix run .#setup" + echo " nix run .#tauri-dev" + echo " nix run .#full-dev" + echo " nix run .#build" + echo " nix run .#test" + echo " nix run .#release-start" ''; }; - } - ); + + apps.info = mkApp "donut-info" '' + set -euo pipefail + echo "Node: $(node --version)" + echo "pnpm: $(pnpm --version)" + echo "Rust: $(rustc --version)" + echo "Cargo: $(cargo --version)" + echo "Tauri CLI: $(cargo-tauri --version)" + ''; + + apps.deps = mkApp "donut-deps" '' + set -euo pipefail + pnpm install + ''; + + apps.dev = mkApp "donut-dev" '' + set -euo pipefail + pnpm dev + ''; + + apps."tauri-dev" = mkApp "donut-tauri-dev" '' + set -euo pipefail + pnpm tauri dev + ''; + + apps."full-dev" = mkApp "donut-full-dev" '' + set -euo pipefail + chmod +x ./scripts/dev.sh + ./scripts/dev.sh + ''; + + apps.build = mkApp "donut-build" '' + set -euo pipefail + pnpm build + (cd src-tauri && cargo build) + ''; + + apps.start = mkApp "donut-start" '' + set -euo pipefail + pnpm start + ''; + + apps.test = mkApp "donut-test" '' + set -euo pipefail + pnpm format && pnpm lint && pnpm test + ''; + + apps.setup = mkApp "donut-setup" '' + set -euo pipefail + + if [ ! -f "package.json" ]; then + echo "package.json not found. Run this from the donutbrowser repo root." + exit 1 + fi + + pnpm install + pnpm copy-proxy-binary + + echo "Setup complete." + echo "Run the app with:" + echo " nix run .#tauri-dev" + echo "Or run full local stack (sync + minio + tauri):" + echo " nix run .#full-dev" + ''; + + apps."release-start" = { + type = "app"; + program = "${releaseLauncher}/bin/donut-release-start"; + }; + + apps.default = self.apps.${system}.setup; + }); }