fix(nix): build from source instead of wrapping AppImage (#778)
* fix(nix): build from source instead of wrapping AppImage (#701) Replace the AppImage-based Nix package with a native from-source build using cargo-tauri.hook + rustPlatform.buildRustPackage. This eliminates the recurring hash mismatch failures when releases update the binary. - Build frontend (bun) and backend (cargo tauri) from source - Vendor JS deps as a fixed-output derivation - Patch libappindicator-sys for Nix store path - Patch ferrous-opencc to skip cbindgen (upstream removed in v0.3.1+) - Wrap binary with WEBKIT_DISABLE_DMABUF_RENDERER and LD_LIBRARY_PATH - Delete update-flake-hash.yml workflow (no longer needed) - devShell unchanged * add support for arm64 --------- Co-authored-by: CJ Pais <cj@cjpais.com>
This commit is contained in:
parent
9ec4f1841b
commit
c34e7d584b
2 changed files with 145 additions and 97 deletions
74
.github/workflows/update-flake-hash.yml
vendored
74
.github/workflows/update-flake-hash.yml
vendored
|
|
@ -1,74 +0,0 @@
|
|||
name: "Update Flake Hash"
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-flake:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
|
||||
- name: Get AppImage hash
|
||||
id: hash
|
||||
env:
|
||||
RELEASE_VERSION: ${{ github.event.release.tag_name }}
|
||||
run: |
|
||||
# Remove 'v' prefix from tag if present
|
||||
VERSION="${RELEASE_VERSION#v}"
|
||||
APPIMAGE_URL="https://github.com/${{ github.repository }}/releases/download/v${VERSION}/Handy_${VERSION}_amd64.AppImage"
|
||||
|
||||
echo "Computing hash for: $APPIMAGE_URL"
|
||||
|
||||
# Retry logic in case AppImage isn't immediately available
|
||||
for i in {1..30}; do
|
||||
HASH=$(nix-prefetch-url --name "Handy_${VERSION}_amd64.AppImage" "$APPIMAGE_URL" 2>&1 | grep "^sha256-" || true)
|
||||
if [ -n "$HASH" ]; then
|
||||
echo "hash=$HASH" >> "$GITHUB_OUTPUT"
|
||||
echo "Successfully computed hash: $HASH"
|
||||
exit 0
|
||||
fi
|
||||
echo "Attempt $i: AppImage not yet available, waiting..."
|
||||
sleep 5
|
||||
done
|
||||
|
||||
echo "Failed to fetch AppImage after retries"
|
||||
exit 1
|
||||
|
||||
- name: Update flake.nix
|
||||
env:
|
||||
HASH: ${{ steps.hash.outputs.hash }}
|
||||
run: |
|
||||
sed -i "s|hash = \"sha256-[^\"]*\"|hash = \"$HASH\"|" flake.nix
|
||||
|
||||
# Verify the hash was actually updated
|
||||
if ! grep -q "hash = \"$HASH\"" flake.nix; then
|
||||
echo "ERROR: Failed to update hash in flake.nix"
|
||||
exit 1
|
||||
fi
|
||||
echo "Successfully updated flake.nix with hash: $HASH"
|
||||
|
||||
- name: Commit and push
|
||||
env:
|
||||
VERSION: ${{ github.event.release.tag_name }}
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
# Only commit if flake.nix actually changed
|
||||
if git diff --quiet flake.nix; then
|
||||
echo "Hash already up-to-date, no commit needed"
|
||||
else
|
||||
git add flake.nix
|
||||
git commit -m "chore(nix): update AppImage hash for ${VERSION}"
|
||||
git push
|
||||
echo "Successfully pushed hash update"
|
||||
fi
|
||||
168
flake.nix
168
flake.nix
|
|
@ -11,7 +11,7 @@
|
|||
nixpkgs,
|
||||
}:
|
||||
let
|
||||
supportedSystems = [ "x86_64-linux" ];
|
||||
supportedSystems = [ "x86_64-linux" "aarch64-linux" ];
|
||||
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||
# Read version from Cargo.toml
|
||||
cargoToml = builtins.fromTOML (builtins.readFile ./src-tauri/Cargo.toml);
|
||||
|
|
@ -22,30 +22,152 @@
|
|||
system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in
|
||||
{
|
||||
# AppImage-based package
|
||||
handy-appimage =
|
||||
let
|
||||
appimage = pkgs.appimageTools.wrapType2 {
|
||||
pname = "handy-appimage-unwrapped";
|
||||
inherit version;
|
||||
src = pkgs.fetchurl {
|
||||
url = "https://github.com/cjpais/Handy/releases/download/v${version}/Handy_${version}_amd64.AppImage";
|
||||
hash = "sha256-tTswFYLCPGtMbHAb2bQMsklRiRCVXLrtu4pQC8IHdqQ=";
|
||||
};
|
||||
extraPkgs =
|
||||
p: with p; [
|
||||
alsa-lib
|
||||
];
|
||||
};
|
||||
in
|
||||
pkgs.writeShellScriptBin "handy" ''
|
||||
export WEBKIT_DISABLE_DMABUF_RENDERER=1
|
||||
exec ${appimage}/bin/handy-appimage-unwrapped "$@"
|
||||
lib = pkgs.lib;
|
||||
|
||||
bunDeps = pkgs.stdenv.mkDerivation {
|
||||
pname = "handy-bun-deps";
|
||||
inherit version;
|
||||
src = self;
|
||||
|
||||
nativeBuildInputs = [
|
||||
pkgs.bun
|
||||
pkgs.cacert
|
||||
];
|
||||
|
||||
dontFixup = true;
|
||||
|
||||
buildPhase = ''
|
||||
export HOME=$TMPDIR
|
||||
bun install --frozen-lockfile --no-progress
|
||||
'';
|
||||
|
||||
default = self.packages.${system}.handy-appimage;
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
cp -r node_modules $out/
|
||||
'';
|
||||
|
||||
outputHashAlgo = "sha256";
|
||||
outputHashMode = "recursive";
|
||||
outputHash = "sha256-+hUANv0w3qnK5d2+4JW3XMazLRDhWCbOxUXQyTGta/0=";
|
||||
};
|
||||
in
|
||||
{
|
||||
handy = pkgs.rustPlatform.buildRustPackage {
|
||||
pname = "handy";
|
||||
inherit version;
|
||||
src = self;
|
||||
|
||||
cargoRoot = "src-tauri";
|
||||
|
||||
cargoLock = {
|
||||
lockFile = ./src-tauri/Cargo.lock;
|
||||
outputHashes = {
|
||||
"rdev-0.5.0-2" = "sha256-0F7EaPF8Oa1nnSCAjzEAkitWVpMldL3nCp3c5DVFMe0=";
|
||||
"rodio-0.20.1" = "sha256-wq72awTvN4fXZ9qZc5KLYS9oMxtNDZ4YGxfqz8msofs=";
|
||||
"tauri-nspanel-2.1.0" = "sha256-gotQQ1DOhavdXU8lTEux0vdY880LLetk7VLvSm6/8TI=";
|
||||
"tauri-runtime-2.9.1" = "sha256-sdneSI2kfRMgTkuqQoFPgtYvkqMPSuoyrffFwOph+ZM=";
|
||||
"vad-rs-0.1.5" = "sha256-Q9Dxq31npyUPY9wwi6OxqSJrEvFvG8/n0dbyT7XNcyI=";
|
||||
};
|
||||
};
|
||||
|
||||
postPatch = ''
|
||||
${pkgs.jq}/bin/jq 'del(.build.beforeBuildCommand) | .bundle.createUpdaterArtifacts = false' \
|
||||
src-tauri/tauri.conf.json > $TMPDIR/tauri.conf.json
|
||||
cp $TMPDIR/tauri.conf.json src-tauri/tauri.conf.json
|
||||
|
||||
# Point libappindicator-sys to the Nix store path
|
||||
substituteInPlace \
|
||||
$cargoDepsCopy/libappindicator-sys-*/src/lib.rs \
|
||||
--replace-fail \
|
||||
"libayatana-appindicator3.so.1" \
|
||||
"${pkgs.libayatana-appindicator}/lib/libayatana-appindicator3.so.1"
|
||||
|
||||
# Disable cbindgen in ferrous-opencc (calls cargo metadata which fails in sandbox)
|
||||
# Upstream removed this call in v0.3.1+
|
||||
substituteInPlace $cargoDepsCopy/ferrous-opencc-0.2.3/build.rs \
|
||||
--replace-fail '.expect("Unable to generate bindings")' '.ok();'
|
||||
substituteInPlace $cargoDepsCopy/ferrous-opencc-0.2.3/build.rs \
|
||||
--replace-fail '.write_to_file("opencc.h");' '// skipped'
|
||||
'';
|
||||
|
||||
preBuild = ''
|
||||
cp -r ${bunDeps}/node_modules node_modules
|
||||
chmod -R +w node_modules
|
||||
patchShebangs node_modules
|
||||
export HOME=$TMPDIR
|
||||
${pkgs.bun}/bin/bun run build
|
||||
'';
|
||||
|
||||
doCheck = false;
|
||||
|
||||
# The tauri hook's installPhase expects target/ in cwd, but our
|
||||
# cargoRoot puts it under src-tauri/. Override to extract the DEB.
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p $out
|
||||
cd src-tauri
|
||||
mv target/${pkgs.stdenv.hostPlatform.rust.rustcTarget}/release/bundle/deb/*/data/usr/* $out/
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
cargo-tauri.hook
|
||||
pkg-config
|
||||
wrapGAppsHook4
|
||||
bun
|
||||
nodejs
|
||||
jq
|
||||
cmake
|
||||
llvmPackages.libclang
|
||||
shaderc
|
||||
];
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
webkitgtk_4_1
|
||||
gtk3
|
||||
glib
|
||||
glib-networking
|
||||
libsoup_3
|
||||
alsa-lib
|
||||
onnxruntime
|
||||
libayatana-appindicator
|
||||
libevdev
|
||||
xorg.libX11
|
||||
xorg.libXtst
|
||||
gtk-layer-shell
|
||||
openssl
|
||||
vulkan-loader
|
||||
vulkan-headers
|
||||
shaderc
|
||||
];
|
||||
|
||||
env = {
|
||||
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
|
||||
BINDGEN_EXTRA_CLANG_ARGS = "-isystem ${pkgs.llvmPackages.libclang.lib}/lib/clang/${lib.getVersion pkgs.llvmPackages.libclang}/include -isystem ${pkgs.glibc.dev}/include";
|
||||
ORT_LIB_LOCATION = "${pkgs.onnxruntime}/lib";
|
||||
OPENSSL_NO_VENDOR = "1";
|
||||
};
|
||||
|
||||
preFixup = ''
|
||||
gappsWrapperArgs+=(
|
||||
--set WEBKIT_DISABLE_DMABUF_RENDERER 1
|
||||
--prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath [
|
||||
pkgs.vulkan-loader
|
||||
pkgs.onnxruntime
|
||||
]}"
|
||||
)
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "A free, open source, and extensible speech-to-text application that works completely offline";
|
||||
homepage = "https://github.com/cjpais/Handy";
|
||||
license = lib.licenses.mit;
|
||||
mainProgram = "handy";
|
||||
platforms = [ "x86_64-linux" ];
|
||||
};
|
||||
};
|
||||
|
||||
default = self.packages.${system}.handy;
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue