Add Nix flake for NixOS support (#561)

* Add Nix flake for NixOS support

Provides:
- handy-appimage: AppImage-based package (default)
- devShell: Development environment for building from source

* fix(nix): address PR feedback for flake.nix

- Remove aarch64-linux from supportedSystems (ARM64 builds not produced)
- Read version dynamically from Cargo.toml instead of hardcoding
  This enables automated updates during GitHub releases

* chore(nix): add GitHub Actions workflow to auto-update AppImage hash

Adds a separate workflow triggered on release publication that:
- Fetches the released AppImage
- Computes the SRI hash using nix-prefetch-url
- Updates flake.nix with the new hash
- Commits and pushes changes directly

Includes retry logic for timing issues and defensive checks to verify
the hash was actually updated and skip commits if unchanged.

* Update flake.nix hash to resolve mismatch

Co-authored-by: pinage404 <pinage404@gmail.com>

* Update flake.nix to install dependencies automatically

Co-authored-by: pinage404 <pinage404@gmail.com>

* fix: update AppImage hash for v0.7.0

* modified description

---------

Co-authored-by: pinage404 <pinage404@gmail.com>
Co-authored-by: CJ Pais <cj@cjpais.com>
This commit is contained in:
Sami Ansari 2026-02-01 02:32:49 +00:00 committed by GitHub
parent 713d0b383e
commit 6827fb51d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 179 additions and 0 deletions

74
.github/workflows/update-flake-hash.yml vendored Normal file
View file

@ -0,0 +1,74 @@
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

27
flake.lock Normal file
View file

@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1767892417,
"narHash": "sha256-dhhvQY67aboBk8b0/u0XB6vwHdgbROZT3fJAjyNh5Ww=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "3497aa5c9457a9d88d71fa93a4a8368816fbeeba",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

78
flake.nix Normal file
View file

@ -0,0 +1,78 @@
{
description = "Handy - A free, open source, and extensible speech-to-text application that works completely offline";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = {
self,
nixpkgs,
}: let
supportedSystems = ["x86_64-linux"];
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
# Read version from Cargo.toml
cargoToml = builtins.fromTOML (builtins.readFile ./src-tauri/Cargo.toml);
version = cargoToml.package.version;
in {
packages = forAllSystems (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 "$@"
'';
default = self.packages.${system}.handy-appimage;
});
# Development shell for building from source
devShells = forAllSystems (system: let
pkgs = nixpkgs.legacyPackages.${system};
in {
default = pkgs.mkShell {
buildInputs = with pkgs; [
# Rust
rustc
cargo
rust-analyzer
clippy
# Frontend
nodejs
bun
# Native deps
pkg-config
openssl
alsa-lib
libsoup_3
webkitgtk_4_1
gtk3
glib
# Tauri CLI
cargo-tauri
];
shellHook = ''
echo "Handy development environment"
bun install
echo "Run 'bun run tauri dev' to start"
'';
};
});
};
}