Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Development Setup

A complete guide for setting up MediaGit for development and contribution.

Prerequisites

ToolVersionPurpose
Rust1.92.0+Language toolchain (MSRV)
Docker20.10+Integration test emulators
Git2.xSource code management

Install Rust

curl --proto '=https' --tlsv1.2 -sSf https://rustup.rs | sh
source ~/.cargo/env

# Install the exact MSRV toolchain
rustup toolchain install 1.92.0
rustup component add rustfmt clippy

Clone and Build

git clone https://github.com/winnyboy5/mediagit-core.git
cd mediagit-core

# Build (debug)
cargo build

# Build release binaries
cargo build --release --bin mediagit --bin mediagit-server

# Install locally
cargo install --path crates/mediagit-cli

Project Structure

mediagit-core/
├── crates/
│   ├── mediagit-cli/          # CLI binary (clap commands)
│   ├── mediagit-versioning/   # ODB, refs, commits, trees, pack files, chunking
│   ├── mediagit-storage/      # LocalBackend + cloud StorageBackend trait
│   ├── mediagit-compression/  # zstd/brotli, smart compression, ObjectType
│   ├── mediagit-media/        # PSD/video/audio/3D format parsers
│   ├── mediagit-config/       # TOML config schema, branch protection
│   ├── mediagit-protocol/     # HTTP push/pull/clone protocol
│   ├── mediagit-server/       # mediagit-server binary
│   ├── mediagit-security/     # AES-GCM encryption, argon2 key derivation
│   ├── mediagit-observability/ # tracing, structured logging
│   ├── mediagit-metrics/      # Prometheus metrics
│   ├── mediagit-migration/    # Repository migration utilities
│   ├── mediagit-git/          # Git interop (smudge/clean filters)
│   └── mediagit-test-utils/   # Shared test helpers (publish = false)
├── book/                      # mdBook documentation source
├── docker/                    # Dockerfiles
├── docker-compose.test.yml    # Storage emulators for integration tests
├── Cargo.lock                 # Committed lockfile (binary workspace)
└── Cargo.toml                 # Workspace root with [workspace.lints]

Running Tests

Unit Tests

# All workspace crates
cargo test --workspace --all-features

# Single crate
cargo test -p mediagit-versioning

# With output (for debugging)
cargo test --workspace -- --nocapture

Integration Tests (requires Docker)

Integration tests are marked #[ignore] and need real storage emulators:

# Start emulators
docker compose -f docker-compose.test.yml up -d

# Run integration tests
export AWS_ACCESS_KEY_ID=minioadmin
export AWS_SECRET_ACCESS_KEY=minioadmin
export AWS_ENDPOINT_URL=http://localhost:9000
export AWS_REGION=us-east-1
export AZURE_STORAGE_CONNECTION_STRING="DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://localhost:10000/devstoreaccount1;"
export GCS_EMULATOR_HOST=http://localhost:4443

cargo test --ignored -p mediagit-storage -p mediagit-server --verbose

# Cleanup
docker compose -f docker-compose.test.yml down -v

MSRV Check

cargo +1.92.0 check --workspace --all-features

Git Hooks (husky-rs)

Git hooks are managed by husky-rs and installed automatically when you cargo build:

HookWhat it enforces
pre-commitcargo fmt --check, cargo clippy --workspace, AGPL license headers, 5MB file size limit, conflict markers
pre-pushcargo test --workspace — all tests must pass
commit-msgConventional Commits format, max 72 chars

To bypass hooks for a WIP: git commit --no-verify To skip in CI: set NO_HUSKY_HOOKS=1 before building.

Code Quality

Formatting

cargo fmt --all

Linting

cargo clippy --workspace --all-targets --all-features -- -D warnings

Workspace lint policy is in Cargo.toml under [workspace.lints]. Three crates (config, security, compression) already inherit these. When adding [lints] workspace = true to other crates, fix any new clippy::all warnings first.

Unused Dependencies

cargo install cargo-machete --locked
cargo machete

License Headers

All .rs files must include an AGPL-3.0 header. Check with:

while IFS= read -r file; do
  grep -q "GNU Affero General Public License" "$file" || echo "MISSING: $file"
done < <(git ls-files 'crates/**/*.rs')

Security Audit

cargo install cargo-audit --locked
cargo audit

Key Coding Patterns

Repository Discovery

#![allow(unused)]
fn main() {
use mediagit_cli::repo::find_repo_root;
let root = find_repo_root().await?;
// Respects MEDIAGIT_REPO env var and -C flag
}

ObjectDatabase Construction

#![allow(unused)]
fn main() {
// Always prefer with_smart_compression()
let odb = ObjectDatabase::with_smart_compression(root).await?;
}

Config Loading

#![allow(unused)]
fn main() {
use mediagit_config::schema::Config;
let config = Config::load(&repo_root).await?;
// Author priority: --author CLI > MEDIAGIT_AUTHOR_NAME env > config.toml [author] > $USER
}

Cross-Platform Paths

#![allow(unused)]
fn main() {
use dunce::canonicalize;
// NOT: std::fs::canonicalize (adds \\?\ prefix on Windows)
let path = dunce::canonicalize(&path)?;
}

Progress Bars

#![allow(unused)]
fn main() {
// Use ProgressTracker from src/progress.rs, not raw indicatif
use mediagit_cli::progress::ProgressTracker;
}

Adding a New CLI Command

  1. Create crates/mediagit-cli/src/commands/mycommand.rs
  2. Derive clap::Parser on your args struct
  3. Add pub mod mycommand; to commands/mod.rs
  4. Add a variant to the Commands enum in main.rs
  5. Wire up execution in main.rs match arm

Adding a New Storage Backend

  1. Implement the Backend trait in mediagit-storage
  2. Add a variant to StorageConfig enum in mediagit-config/src/schema.rs
  3. Wire up construction in the storage factory
  4. Add integration tests with #[ignore] and an emulator
  5. Document in book/src/architecture/backend-*.md

Benchmarks

cargo bench --workspace

Benchmarks use criterion. Results are stored in target/criterion/.

Documentation

# Install mdbook
cargo install mdbook
# Install mdbook-mermaid (pre-built binary, much faster)
MERMAID_VERSION="0.14.0"
curl -fsSL "https://github.com/badboy/mdbook-mermaid/releases/download/v${MERMAID_VERSION}/mdbook-mermaid-v${MERMAID_VERSION}-x86_64-unknown-linux-gnu.tar.gz" \
  | tar xz -C ~/.cargo/bin/

# Serve docs locally with live reload
cd book
mdbook serve
# Open http://localhost:3000

WSL2 Notes

When developing on Windows via WSL2 with the repository on an NTFS mount:

  • Cargo’s fingerprinting may not detect file changes reliably on NTFS mounts
  • Prefer cloning to a WSL2-native path (e.g. ~/projects/mediagit-core) for reliable incremental builds
  • The Linux ELF binary at target/release/mediagit works from WSL2
  • The Windows PE binary (mediagit.exe) requires building from Windows cmd or PowerShell

Troubleshooting

Git hooks not executing

git commit (or git push) fails with:

fatal: cannot exec '.husky/pre-commit': No such file or directory

This can happen on any platform — Windows, Linux, macOS, or CI — when either of two conditions are present:

Cause 1 — CRLF line endings

git config core.autocrlf true (the Windows git default) converts \n\r\n in text files on checkout. The hook shebang becomes #!/bin/sh\r, and the kernel cannot find an interpreter named sh followed by a carriage return.

Diagnose: file .husky/pre-commit — reports “with CRLF line terminators” if affected.

Fix:

sed -i 's/\r//' .husky/pre-commit .husky/pre-push .husky/commit-msg

Cause 2 — Missing execute bit

Filesystems that do not track Unix permissions (NTFS, FAT32, SMB shares, CI artifact zip extracts) may drop the +x bit on checkout.

Diagnose: ls -la .husky/ — hook files should be -rwxr-xr-x, not -rw-r--r--.

Fix:

chmod +x .husky/pre-commit .husky/pre-push .husky/commit-msg
git update-index --chmod=+x .husky/pre-commit .husky/pre-push .husky/commit-msg

git update-index --chmod=+x records mode 100755 in the git index so the bit is preserved for future checkouts on the same machine.

Combined fix (safe to run on any platform after a fresh clone):

sed -i 's/\r//' .husky/pre-commit .husky/pre-push .husky/commit-msg
chmod +x        .husky/pre-commit .husky/pre-push .husky/commit-msg
git update-index --chmod=+x .husky/pre-commit .husky/pre-push .husky/commit-msg

The .gitattributes file at the repo root enforces eol=lf for .husky/*, which prevents the CRLF issue from recurring on subsequent checkouts.

Getting Help