Skip to content

nutthead/samoyed

Repository files navigation

Samoyed

Crates.io Version

A single-binary, minimalist, ultra-fast Git hooks manager for every platform.

Samoyed keeps Git hook management small, transparent, and safe. It ships as one Rust binary plus a POSIX wrapper script, so developers can install it quickly, version it with their repositories, and stay in control of what runs on commit.

Samoyed

Why Samoyed?

  • Single binary — Zero runtime dependencies. One Rust executable embeds everything.
  • Transparent — All code in one file (src/main.rs). No hidden complexity.
  • Cross-platform — Works on Linux, macOS, and Windows (WSL). POSIX wrapper ensures consistency.
  • Developer-friendlySAMOYED=0 to bypass, SAMOYED=2 to debug. Simple escape hatches when you need them.
  • 80% smaller — 0.2.x radically simplifies the code from 6000+ lines across 23 modules to ~1000 lines of code in one file.

Quick Start

Get started in under a minute:

# Install Samoyed
curl -fsSL https://raw.githubusercontent.com/nutthead/samoyed/main/install.sh | bash

# Navigate to your Git repository
cd your-project

# Initialize hooks
samoyed init

# Edit the starter pre-commit hook
$EDITOR .samoyed/pre-commit

# Test it
git commit --allow-empty -m "test hooks"

Table of Contents

Install

Quick Install (Linux and macOS)

The fastest way to install Samoyed is with a single command:

curl -fsSL https://raw.githubusercontent.com/nutthead/samoyed/main/install.sh | bash

This downloads the latest pre-built binary for your platform, verifies its checksum, and places it in ~/.local/bin. To customize the installation directory:

curl -fsSL https://raw.githubusercontent.com/nutthead/samoyed/main/install.sh | INSTALL_DIR=~/bin bash

If you prefer to inspect the installer before running it:

curl -fsSL https://raw.githubusercontent.com/nutthead/samoyed/main/install.sh -o install.sh
less install.sh
bash install.sh

Using Cargo

If you have Rust installed, use cargo install:

cargo install samoyed

Building from Source

Clone the repository and build locally:

git clone https://github.com/nutthead/samoyed.git
cd samoyed
cargo install --path .

Windows

Windows users should install via WSL (Windows Subsystem for Linux) and follow the Linux installation instructions above, or use cargo install samoyed in a native Windows terminal with the Rust toolchain installed.

Uninstalling

To remove Samoyed:

curl -fsSL https://raw.githubusercontent.com/nutthead/samoyed/main/uninstall.sh | bash

Or manually:

rm ~/.local/bin/samoyed
# Remove PATH entry from your shell config if added by the installer

Usage

Initialize Hooks

Inside your Git repository, run:

samoyed init [samoyed-dirname]

The samoyed-dirname defaults to .samoyed and must reside within the repository. On success, Samoyed creates:

.samoyed/
├── _/                    # Hook wrappers (git-ignored)
│   ├── pre-commit
│   ├── commit-msg
│   ├── pre-push
│   ├── ... (all 14 hooks)
│   └── samoyed          # POSIX wrapper script
├── pre-commit           # Your editable hook (starter template)
└── .gitignore           # Ignores the _/ directory

Git's core.hooksPath is configured to point to .samoyed/_/, routing all hook events through the wrapper.

Creating Your First Hook

The starter pre-commit script includes helpful comments. Edit it to add project-specific checks:

#!/bin/sh
# .samoyed/pre-commit

# Example: Format Rust code before commit
if ! cargo fmt --check; then
  echo "Running cargo fmt..."
  cargo fmt
  git add --update '*.rs'
fi

# Example: Run linter
cargo clippy -- -D warnings

# Example: Ensure tests pass
cargo test --all

Make it executable and commit it to version control:

chmod +x .samoyed/pre-commit
git add .samoyed/
git commit -m "Add pre-commit hook for Rust formatting and tests"

Now every commit will automatically run these checks.

Bypass and Debug Modes

Bypass all hooks when you need to commit without running checks:

SAMOYED=0 git commit -m "emergency fix"

Enable debug mode to see exactly what the wrapper is doing:

SAMOYED=2 git commit -m "debug this hook"

This runs the wrapper with set -x, printing each command as it executes.

Bypass during initialization to prepare hooks without activating them:

SAMOYED=0 samoyed init

Configuration

User Init Script

Samoyed sources an optional initialization script at ${XDG_CONFIG_HOME:-$HOME/.config}/samoyed/init.sh before running any hook. Use this for environment setup shared across all hooks:

# ~/.config/samoyed/init.sh

# Load secrets
export DATABASE_URL="$(cat ~/.secrets/db_url)"

# Set project defaults
export RUST_BACKTRACE=1

# Skip hooks in CI (optional)
if [ -n "$CI" ]; then
  exit 0
fi

Per-Hook Customization

Because hooks are standard shell scripts, customize them directly in .samoyed/<hook>:

# .samoyed/pre-push - runs before git push

# Ensure main branch is never pushed directly
branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,')
if [ "$branch" = "main" ]; then
  echo "Direct pushes to main are forbidden"
  exit 1
fi

# Run full test suite before pushing
cargo test --all --release

Background

Samoyed was built to strip Git hook tooling down to the essentials:

  • One file of Rust code (src/main.rs) manages CLI parsing, repository safety checks, and file generation.
  • One POSIX shell wrapper (assets/samoyed) bootstraps every Git hook and keeps behaviour consistent across macOS, Linux, and Windows (via WSL or compatible shells).
  • Zero runtime dependencies. The compiled binary embeds the wrapper---assets/samoyed---with include_bytes!, so distributing Samoyed is as simple as copying the executable.

In 0.2.x, Samoyed doubles down on clarity: the samoyed init command seeds every Git hook, wires them through the shared wrapper, and leaves a template pre-commit script ready for teams to adapt. Environment variables such as SAMOYED=0 (bypass) and SAMOYED=2 (debug) give developers predictable escape hatches without extra plugins.

This represents a fundamental architectural simplification from version 0.1.17, which scattered functionality across 23 separate Rust modules totaling nearly 6,000 lines of code. The current single-file implementation achieves the same functionality* in just about 1000 lines of code---an ~80% reduction in code size. By consolidating everything into src/main.rs, the codebase becomes dramatically easier to understand, debug, and maintain, while eliminating the cognitive overhead of navigating complex module hierarchies and cross-file dependencies.

*Support for samoyed.toml is removed in version 0.2.0. However I will re-introduce a well-thought-out option for configuring hooks "declaratively" in a future release.

Development

Clone the repository and work inside a Nix shell (nix develop) or your local Rust toolchain.

Core commands:

cargo check           # Fast type checks
cargo fmt             # Format Rust code
cargo build --release # Build the optimised binary used in production
cargo test            # Run unit tests

Integration tests live under tests/integration. Each script provisions a disposable repository via mktemp; run them individually, optionally preserving the workspace for inspection:

cd tests/integration
./01_default.sh            # Run a single scenario
./08_samoyed_0.sh --keep   # Keep the temporary repo after the test exits

Maintainers

Contributing

Issues and pull requests are welcome. Before submitting a change, please ensure:

  1. cargo fmt, cargo check, and cargo test pass locally.
  2. Relevant integration scripts succeed.
  3. Commit messages follow Conventional Commit style (feat:, fix:, chore:, …).

License

MIT

About

A minimalist Rust-based alternative to Husky

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

  •  

Contributors 5