diff --git a/pkgs/by-name/ha/handy/package.nix b/pkgs/by-name/ha/handy/package.nix index 8b0403fa158e5..feed4136a6ad2 100644 --- a/pkgs/by-name/ha/handy/package.nix +++ b/pkgs/by-name/ha/handy/package.nix @@ -10,10 +10,10 @@ cctools, cargo-tauri, jq, - llvmPackages, writableTmpDirAsHomeHook, makeBinaryWrapper, swift, + apple-sdk_26, # Linux-only webkitgtk_4_1, @@ -55,8 +55,17 @@ rustPlatform.buildRustPackage ( ] ); - # Bun node_modules as a fixed-output derivation; --production is intentionally - # omitted because devDeps (e.g. @types/*) are required for `tsc` at build time. + # Per-platform bun node_modules hashes; --production is intentionally + # omitted because devDeps (e.g. @types/*) are required for tsc at + # build time. Bun downloads host-matching native binaries only, so + # each system has a distinct hash. + # Details: https://github.com/cjpais/Handy/pull/1256 + # TODO: switch to bun.fetchDeps once NixOS/nixpkgs#376299 is merged. + frontendDepsHashes = { + "x86_64-linux" = "sha256-tJ6LK99dELOiR0BcsTRTt/vLyNamntujLxhBy5Xl/lc="; + "aarch64-linux" = "sha256-S+dX6ZVgv9dexxIHoa5PxP7e0nxf/d7cKUGty5eEi8A="; + "aarch64-darwin" = "sha256-DQbogNBQ9izK5GPmoOudqiB2lJvct1vZI2U5lp3WFy8="; + }; frontendDeps = stdenv.mkDerivation { pname = "${finalAttrs.pname}-frontend-deps"; inherit (finalAttrs) version src; @@ -68,7 +77,9 @@ rustPlatform.buildRustPackage ( buildPhase = '' runHook preBuild export BUN_INSTALL_CACHE_DIR=$(mktemp -d) - bun install --force --frozen-lockfile --ignore-scripts --no-progress + bun install --linker=isolated --force --frozen-lockfile \ + --ignore-scripts --no-progress + bun --bun "$PWD/.nix/scripts/normalize-install.ts" runHook postBuild ''; installPhase = '' @@ -78,13 +89,19 @@ rustPlatform.buildRustPackage ( runHook postInstall ''; dontFixup = true; - outputHash = "sha256-AxE6WuFOGk40h+2w3Tyvnh64Eb5yHA5EZWgemYafRhg="; + outputHash = + frontendDepsHashes.${stdenv.hostPlatform.system} or (throw '' + handy: no frontendDeps hash for ${stdenv.hostPlatform.system}. + Run `nix-update --flake handy` (or the equivalent `passthru.updateScript`) + on a host of that system and paste the value into + pkgs/by-name/ha/handy/package.nix:frontendDepsHashes. + ''); outputHashMode = "recursive"; }; in { pname = "handy"; - version = "0.8.2"; + version = "0.8.3"; __structuredAttrs = true; @@ -92,14 +109,14 @@ rustPlatform.buildRustPackage ( owner = "cjpais"; repo = "Handy"; tag = "v${finalAttrs.version}"; - hash = "sha256-X21uFe609Vitv7yvFMfT847a4E2gy3Uy/uPh1I8D7pA="; + hash = "sha256-sCCtp6UAxmCAcYeOM9+RW2czATh4Geqf1H8wXNMniYc="; }; cargoRoot = "src-tauri"; - cargoHash = "sha256-qwcKuPfSLVmjIkduKkIRCmVk6BPbxF5htfY6f+6yV0w="; + cargoHash = "sha256-mvOThNqfE24iMkVBM2zYexJkQxpMMgE4PPNXKy39hSg="; postPatch = '' - # Strip updater artifacts; disable macOS code-signing (no identity in sandbox) + # Strip updater artifacts; disable macOS code-signing in sandbox ${jq}/bin/jq ' del(.build.beforeBuildCommand) | .bundle.createUpdaterArtifacts = false | @@ -111,7 +128,7 @@ rustPlatform.buildRustPackage ( ${jq}/bin/jq 'del(.scripts.postinstall)' package.json > $TMPDIR/package.json cp $TMPDIR/package.json package.json - # cbindgen calls `cargo metadata` which fails in the Nix sandbox. + # cbindgen's cargo metadata fails in the sandbox find $cargoDepsCopy -path "*/ferrous-opencc-*/build.rs" \ -exec sed -i \ -e '/cbindgen::Builder::new/{:l;/write_to_file/!{N;bl};d}' \ @@ -122,9 +139,6 @@ rustPlatform.buildRustPackage ( -exec sed -i \ 's|libayatana-appindicator3.so.1|${libayatana-appindicator}/lib/libayatana-appindicator3.so.1|' \ {} \; - '' - + lib.optionalString stdenv.hostPlatform.isDarwin '' - patch -p1 < ${./use-nix-swift.patch} ''; nativeBuildInputs = [ @@ -134,7 +148,7 @@ rustPlatform.buildRustPackage ( nodejs cargo-tauri.hook jq - llvmPackages.libclang + rustPlatform.bindgenHook ] ++ lib.optionals stdenv.hostPlatform.isLinux [ wrapGAppsHook4 @@ -144,6 +158,7 @@ rustPlatform.buildRustPackage ( makeBinaryWrapper cctools swift + apple-sdk_26 # optional; enables Apple Intelligence on macOS 26+ ]; buildInputs = [ @@ -168,13 +183,6 @@ rustPlatform.buildRustPackage ( ++ gstPlugins; env = { - LIBCLANG_PATH = "${llvmPackages.libclang.lib}/lib"; - BINDGEN_EXTRA_CLANG_ARGS = lib.concatStringsSep " " ( - [ - "-isystem ${llvmPackages.libclang.lib}/lib/clang/${lib.getVersion llvmPackages.libclang}/include" - ] - ++ lib.optional stdenv.hostPlatform.isLinux "-isystem ${stdenv.cc.libc.dev}/include" - ); ORT_LIB_LOCATION = "${onnxruntime}/lib"; ORT_PREFER_DYNAMIC_LINK = "1"; GST_PLUGIN_SYSTEM_PATH_1_0 = lib.optionalString stdenv.hostPlatform.isLinux ( @@ -225,22 +233,22 @@ rustPlatform.buildRustPackage ( ]; } }" - --prefix LD_LIBRARY_PATH : "${ - lib.makeLibraryPath [ - vulkan-loader - onnxruntime - ] - }" ) ''; - # Bake the onnxruntime dylib path into the binary's rpath so it can be found - # at runtime without relying on the infamous DYLD_LIBRARY_PATH (blocked by SIP on macOS). + # onnxruntime rpath (DYLD_LIBRARY_PATH is blocked by SIP on macOS) postFixup = lib.optionalString stdenv.hostPlatform.isDarwin '' install_name_tool -add_rpath ${onnxruntime}/lib \ "$out/Applications/Handy.app/Contents/MacOS/handy" ''; + passthru = { + inherit frontendDeps; + # nix-update-script cannot manage per-platform frontendDepsHashes; + # this wraps nix-update and refreshes the current host's entry. + updateScript = ./update.sh; + }; + meta = { description = "Free, open source, offline speech-to-text application"; longDescription = '' diff --git a/pkgs/by-name/ha/handy/update.sh b/pkgs/by-name/ha/handy/update.sh new file mode 100755 index 0000000000000..74e44c2d0ac7f --- /dev/null +++ b/pkgs/by-name/ha/handy/update.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i bash -p nix-update nix gnused gnugrep gawk coreutils git +# shellcheck shell=bash + +# 1. `nix-update handy` bumps version, src.hash, cargoHash. +# 2. On a version bump, all frontendDepsHashes entries are reset to lib.fakeHash. +# 3. Rebuilds handy.passthru.frontendDeps to capture the real hash for the current host. +# 4. Remaining entries stay lib.fakeHash — re-run on each target host to fill them in. + +set -euo pipefail + +repo_root=$(git rev-parse --show-toplevel) +pkg_file="$repo_root/pkgs/by-name/ha/handy/package.nix" +cd "$repo_root" + +if [[ ! -f "$pkg_file" ]]; then + echo "update.sh: $pkg_file not found" >&2 + exit 1 +fi + +read_version() { + sed -n 's|^\s*version = "\([^"]*\)";.*|\1|p' "$pkg_file" | head -n1 +} + +nix_update_args=(handy) +if [[ $# -gt 0 ]]; then + nix_update_args+=(--version "$1") +fi + +old_version=$(read_version) +nix-update "${nix_update_args[@]}" +new_version=$(read_version) + +if [[ "$old_version" != "$new_version" ]]; then + echo "update.sh: version bump $old_version → $new_version; resetting frontendDepsHashes" + sed -i -e '/frontendDepsHashes = {/,/^ };$/ s|"sha256-[^"]*"|lib.fakeHash|g' "$pkg_file" +fi + +system=$(nix --extra-experimental-features nix-command eval --impure --raw --expr 'builtins.currentSystem') + +# Force lib.fakeHash so the build reports the real hash via "got:" even +# when the existing value happens to still be correct. +sed -i "s|\"$system\" = \"sha256-[^\"]*\";|\"$system\" = lib.fakeHash;|" "$pkg_file" + +if ! grep -q "\"$system\" = lib.fakeHash;" "$pkg_file"; then + echo "update.sh: no frontendDepsHashes entry for $system to refresh" >&2 + exit 1 +fi + +echo "update.sh: rebuilding frontendDeps on $system" +build_out=$(nix-build -A handy.passthru.frontendDeps --no-out-link 2>&1 || true) +new_hash=$(awk '/got:/ { print $2; exit }' <<<"$build_out") +if [[ -z "${new_hash:-}" ]]; then + echo "update.sh: failed to parse the new hash from nix-build output" >&2 + printf '%s\n' "$build_out" | tail -n40 >&2 + exit 1 +fi + +sed -i "s|\"$system\" = lib.fakeHash;|\"$system\" = \"$new_hash\";|" "$pkg_file" + +echo "update.sh: $system → $new_hash" +if [[ "$old_version" != "$new_version" ]]; then + echo "update.sh: other frontendDepsHashes entries are still lib.fakeHash;" + echo "update.sh: re-run on each target host to fill them in." +fi diff --git a/pkgs/by-name/ha/handy/use-nix-swift.patch b/pkgs/by-name/ha/handy/use-nix-swift.patch deleted file mode 100644 index 36f9c2222bb49..0000000000000 --- a/pkgs/by-name/ha/handy/use-nix-swift.patch +++ /dev/null @@ -1,60 +0,0 @@ ---- a/src-tauri/build.rs 2026-04-07 08:26:23.610240094 -0400 -+++ b/src-tauri/build.rs 2026-04-07 10:35:13.241879297 -0400 -@@ -129,16 +129,18 @@ - let object_path = out_dir.join("apple_intelligence.o"); - let static_lib_path = out_dir.join("libapple_intelligence.a"); - -- let sdk_path = String::from_utf8( -- Command::new("xcrun") -- .args(["--sdk", "macosx", "--show-sdk-path"]) -- .output() -- .expect("Failed to locate macOS SDK") -- .stdout, -- ) -- .expect("SDK path is not valid UTF-8") -- .trim() -- .to_string(); -+ let sdk_path = env::var("SDKROOT").unwrap_or_else(|_| { -+ String::from_utf8( -+ Command::new("xcrun") -+ .args(["--sdk", "macosx", "--show-sdk-path"]) -+ .output() -+ .expect("Failed to locate macOS SDK") -+ .stdout, -+ ) -+ .expect("SDK path is not valid UTF-8") -+ .trim() -+ .to_string() -+ }); - - // Check if the SDK supports FoundationModels (required for Apple Intelligence) - let framework_path = -@@ -157,16 +159,7 @@ - panic!("Source file {} is missing!", source_file); - } - -- let swiftc_path = String::from_utf8( -- Command::new("xcrun") -- .args(["--find", "swiftc"]) -- .output() -- .expect("Failed to locate swiftc") -- .stdout, -- ) -- .expect("swiftc path is not valid UTF-8") -- .trim() -- .to_string(); -+ let swiftc_path = env::var("SWIFTC").unwrap_or_else(|_| "swiftc".to_string()); - - let toolchain_swift_lib = Path::new(&swiftc_path) - .parent() -@@ -178,9 +171,8 @@ - // Use macOS 11.0 as deployment target for compatibility - // The @available(macOS 26.0, *) checks in Swift handle runtime availability - // Weak linking for FoundationModels is handled via cargo:rustc-link-arg below -- let status = Command::new("xcrun") -+ let status = Command::new(&swiftc_path) - .args([ -- "swiftc", - "-target", - "arm64-apple-macosx11.0", - "-sdk",