Development Setup
A complete guide for setting up MediaGit for development and contribution.
Prerequisites
| Tool | Version | Purpose |
|---|---|---|
| Rust | 1.92.0+ | Language toolchain (MSRV) |
| Docker | 20.10+ | Integration test emulators |
| Git | 2.x | Source 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:
| Hook | What it enforces |
|---|---|
pre-commit | cargo fmt --check, cargo clippy --workspace, AGPL license headers, 5MB file size limit, conflict markers |
pre-push | cargo test --workspace — all tests must pass |
commit-msg | Conventional 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
- Create
crates/mediagit-cli/src/commands/mycommand.rs - Derive
clap::Parseron your args struct - Add
pub mod mycommand;tocommands/mod.rs - Add a variant to the
Commandsenum inmain.rs - Wire up execution in
main.rsmatch arm
Adding a New Storage Backend
- Implement the
Backendtrait inmediagit-storage - Add a variant to
StorageConfigenum inmediagit-config/src/schema.rs - Wire up construction in the storage factory
- Add integration tests with
#[ignore]and an emulator - 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/mediagitworks from WSL2 - The Windows PE binary (
mediagit.exe) requires building from Windowscmdor 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
- Check existing issues: GitHub Issues
- Review CONTRIBUTING.md
- Read the Architecture Overview