Crates.io | samoyed |
lib.rs | samoyed |
version | 0.1.17 |
created_at | 2025-08-01 19:49:56.532186+00 |
updated_at | 2025-08-15 08:58:03.074129+00 |
description | A single-binary Git hooks manager |
homepage | https://github.com/nutthead/samoyed |
repository | https://github.com/nutthead/samoyed |
max_upload_size | |
id | 1777472 |
size | 157,059 |
A modern, fast, and secure Git hooks manager written in Rust. Samoyed is inspired by Husky with improved performance, better error handling, and enhanced security features.
You don't have to fuss with that pesky package.json
file in your projects anymore! 🤌
Signing keys are rotated every 10 years or immediately upon suspected compromise. Rotation involves generating new keys, updating GitHub secrets, and notifying users via security advisory.
Samoyed is published on crates.io:
cargo install samoyed
All Samoyed release binaries are cryptographically signed with GPG to ensure authenticity and integrity. This protects against tampering and supply chain attacks.
For users who want to quickly verify a download:
# Download and import our public key
curl -sL https://github.com/nutthead/samoyed/releases/latest/download/samoyed-release-public.key | gpg --import
# Verify a binary (replace with your downloaded file)
gpg --verify samoyed-0.1.10-x86_64-unknown-linux-gnu.tar.gz.asc \
samoyed-0.1.10-x86_64-unknown-linux-gnu.tar.gz
Download and import our signing key from the latest release:
# Download the public key
curl -sL https://github.com/nutthead/samoyed/releases/latest/download/samoyed-release-public.key -o samoyed-release-public.key
# Import the key into your GPG keyring
gpg --import samoyed-release-public.key
Critical Security Step: Always verify the key fingerprint matches our official key:
gpg --fingerprint 02D1B70CF6D841EEE6876E13F7A6F8331CBBC51F
Expected output should show:
pub ed25519 2025-01-01 [SC] [expires: 2035-01-01]
02D1 B70C F6D8 41EE E687 6E13 F7A6 F833 1CBB C51F
uid Behrang Saeedzadeh <hello@behrang.org>
sub ed25519 2025-01-01 [E] [expires: 2035-01-01]
⚠️ Security Warning: If the fingerprint doesn't match exactly, do not proceed. This could indicate a compromised release.
Download the binary and its signature from the releases page:
# Example for Linux x86_64 (adjust for your platform)
wget https://github.com/nutthead/samoyed/releases/download/v0.1.10/samoyed-0.1.10-x86_64-unknown-linux-gnu.tar.gz
wget https://github.com/nutthead/samoyed/releases/download/v0.1.10/samoyed-0.1.10-x86_64-unknown-linux-gnu.tar.gz.asc
gpg --verify samoyed-0.1.10-x86_64-unknown-linux-gnu.tar.gz.asc \
samoyed-0.1.10-x86_64-unknown-linux-gnu.tar.gz
Expected Output (successful verification):
gpg: Signature made [date] using EdDSA key ID 1CBBC51F
gpg: Good signature from "Behrang Saeedzadeh <hello@behrang.org>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
The warning is normal unless you've explicitly trusted our key in your keyring.
For additional security, verify the SHA256 checksums:
# Download checksums and signature
wget https://github.com/nutthead/samoyed/releases/download/v0.1.10/CHECKSUMS.txt
wget https://github.com/nutthead/samoyed/releases/download/v0.1.10/CHECKSUMS.txt.asc
# Verify checksums signature
gpg --verify CHECKSUMS.txt.asc CHECKSUMS.txt
# Verify your binary's checksum
sha256sum -c CHECKSUMS.txt --ignore-missing
Linux distributions typically have GPG pre-installed. If not:
# Ubuntu/Debian
sudo apt update && sudo apt install gnupg
# RHEL/CentOS/Fedora
sudo dnf install gnupg2
# or for older systems: sudo yum install gnupg2
# Arch Linux
sudo pacman -S gnupg
Install GPG using Homebrew:
# Install Homebrew if not already installed
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install GPG
brew install gnupg
Alternative: Download GPG Suite for a GUI experience.
Option 1: GPG4Win (Recommended)
gpg
command in Command Prompt/PowerShellOption 2: Git for Windows If you have Git for Windows installed, GPG is included:
# Use Git Bash terminal
gpg --version
Option 3: Windows Subsystem for Linux (WSL)
# In WSL terminal
sudo apt update && sudo apt install gnupg
PowerShell Example:
# Import key
gpg --import samoyed-release-public.key
# Verify signature
gpg --verify samoyed-0.1.10-x86_64-pc-windows-msvc.zip.asc samoyed-0.1.10-x86_64-pc-windows-msvc.zip
Cause: GPG is not installed or not in your PATH.
Solutions:
brew install gnupg
Cause: You haven't imported our public key yet.
Solution:
# Import the public key first
curl -sL https://github.com/nutthead/samoyed/releases/latest/download/samoyed-release-public.key | gpg --import
Cause: The file has been tampered with or corrupted.
Solutions:
github.com/nutthead/samoyed
Cause: This is normal behavior. GPG warns when you haven't explicitly trusted a key.
This is NOT an error. The important part is seeing "Good signature from...".
To remove the warning (optional):
# Trust our key (do this only after verifying the fingerprint)
gpg --edit-key 02D1B70CF6D841EEE6876E13F7A6F8331CBBC51F
# Type "trust" then "5" (ultimate trust) then "y" then "quit"
Cause: GPG agent configuration issue (usually on headless systems).
Solution:
export GPG_TTY=$(tty)
# Add to your ~/.bashrc or ~/.zshrc to make permanent
Cause: You're trying to verify a file without downloading its .asc
signature.
Solution: Always download both files:
# Download binary
wget https://github.com/nutthead/samoyed/releases/download/v0.1.10/samoyed-0.1.10-linux.tar.gz
# Download signature
wget https://github.com/nutthead/samoyed/releases/download/v0.1.10/samoyed-0.1.10-linux.tar.gz.asc
Cause: Very old GPG versions (< 2.1) may not support Ed25519 keys.
Solution:
gpg --version
Cause: Firewall or network restrictions.
Alternative methods:
# Method 1: Direct download
wget https://github.com/nutthead/samoyed/releases/latest/download/samoyed-release-public.key
# Method 2: From repository
wget https://raw.githubusercontent.com/nutthead/samoyed/main/samoyed-release-public.key
# Method 3: Manual import (copy key content and save to file)
cat > samoyed-release-public.key << 'EOF'
[paste key content here]
EOF
github.com/nutthead/samoyed
For security-related issues or questions about signature verification:
⚠️ Important: Samoyed has migrated from a dual-binary architecture (samoyed
+ samoyed-hook
) to a unified single binary (samoyed
with hook
subcommand).
exec samoyed-hook "pre-commit" "$@"
exec samoyed hook "pre-commit" "$@"
Update Samoyed: Install the latest version
cargo install samoyed
Re-initialize Your Hooks: This updates your .samoyed/_/*
files to use the new unified binary
# Option 1: Standard re-initialization (recommended)
# Safe to run multiple times, updates hooks only if needed
samoyed init
# Option 2: Force re-initialization (explicit migration)
# Explicitly overwrites all hook files to ensure complete migration
samoyed init -f _
When to use each option:
samoyed init
for normal migration (safe, non-destructive)samoyed init -f _
if you want to force complete re-creation of all hook filesVerify Migration: Check that your hook files now reference samoyed hook
cat .samoyed/_/pre-commit
# Should show: exec samoyed hook "$(basename "$0")" "$@"
samoyed-hook
binary still works but shows deprecation warningssamoyed-hook
binary will be removed entirelyIf you encounter issues:
samoyed --version
samoyed init
to refresh all hook filessamoyed hook --help
worksInitialize Git hooks in your repository:
samoyed init
This will:
.samoyed/_
as the hooks directorysamoyed
binary# Initialize hooks (one-time setup)
samoyed init
# Install hooks with custom directory
samoyed init --hooks-dir custom-hooks
Samoyed uses a three-layer architecture that provides both flexibility and performance:
samoyed
: Unified binary for both CLI interface and hook executionsequenceDiagram
participant Git as Git
participant Hook as .samoyed/_/pre-commit
participant Runner as samoyed hook
participant Config as samoyed.toml
participant Script as .samoyed/scripts/pre-commit
participant Shell as Shell
Git->>Hook: git commit triggers
Hook->>Runner: exec samoyed hook "pre-commit" "$@"
Runner->>Config: 1. Check for [hooks] pre-commit
alt Command found in samoyed.toml
Config-->>Runner: "cargo fmt --check && cargo clippy"
Runner->>Shell: Execute command via shell
Shell-->>Runner: Exit code
else No command in samoyed.toml
Runner->>Script: 2. Look for .samoyed/scripts/pre-commit
alt Script exists
Runner->>Script: Execute script file
Script-->>Runner: Exit code
else No script found
Runner-->>Git: Exit silently (0)
end
end
Runner-->>Git: Propagate exit code
When you run samoyed init
, three key components are created:
samoyed.toml
- Primary ConfigurationRaison d'être: The primary configuration mechanism where you define commands for each hook.
[hooks]
pre-commit = "cargo fmt --check && cargo clippy -- -D warnings"
pre-push = "cargo test --release"
.samoyed/_/
- Git Hook Delegation LayerRaison d'être: Git integration. These files tell Git "when you want to run a hook, call samoyed hook instead."
All files contain identical delegation code:
#!/usr/bin/env sh
exec samoyed hook "$(basename "$0")" "$@"
Git's core.hooksPath=.samoyed/_
points here, so git commit
→ .samoyed/_/pre-commit
→ samoyed hook
.
.samoyed/scripts/
- Fallback & ExamplesRaison d'être: Fallback mechanism for complex scenarios and examples for users who prefer script files.
The hook runner implements a sophisticated two-tier lookup:
samoyed.toml
for a command string.samoyed/scripts/
This provides maximum flexibility:
SAMOYED=0
- Skip all hook execution (useful for CI/deployment)SAMOYED=1
- Normal execution mode (default)SAMOYED=2
- Enable debug mode with detailed script tracingThis architecture ensures minimal overhead:
samoyed hook
# Build debug version
cargo build
# Build release version
cargo build --release
# Run tests
cargo test
# Run benchmarks
cargo bench
The project uses comprehensive testing with dependency injection:
# Run all tests
cargo test
# Run specific test categories
cargo test --test installation_tests
cargo test --test validation_tests
cargo test --test error_handling_tests
# Run platform-specific tests
cargo test --test linux_tests # Linux only
cargo test --test macos_tests # macOS only
cargo test --test windows_tests # Windows only
Generate coverage reports:
cargo tarpaulin --verbose --bins --all-features
Tarpaulin is configured in .tarpaulin.toml to store reports in <target/tarpaulin/coverage/>.
This project is licensed under the MIT License - see the LICENSE file for details.