Skip to content

Shiva210Jyoti/Hot-Reload-Engine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🔥 HotReload — Production-Grade Hot Reload Engine for Go

Go Version Coverage License

A blazingly fast, production-ready hot reload engine for Go projects — with health checks, crash loop protection, a real-time dashboard, and more. No Air. No Realize. No hidden magic.


✨ Features

Core Features (100% Requirements Met)

Feature Description
Sub-2-Second Reloads Optimized rebuild and restart pipeline
Intelligent Debouncing Handles editor save bursts gracefully (default 300ms)
Nested Directory Support Watches the entire project tree recursively
Graceful Process Management Clean shutdown with SIGTERM → SIGKILL escalation
Real-Time Log Streaming See build and server logs instantly — zero buffering
Crash Loop Protection Exponential backoff prevents restart storms

Advanced Features (Going Beyond)

Feature Description
🌐 Web Dashboard Real-time build status at http://localhost:3000
🏥 HTTP Health Checks Verify the server actually started before marking OK
🔔 Desktop Notifications Cross-platform alerts on build success/failure
🧠 Build Fingerprinting Skip rebuilds when only timestamps change
📋 YAML Configuration .hotreload.yaml — no more long CLI commands
🎨 Beautiful Terminal Output Color-coded, structured slog component logs
🔍 Smart Filtering .gitignore integration + custom glob patterns

🚀 Quick Start

Installation

# Clone and build
git clone https://github.com/Shiva210Jyoti/hot-reload-engine
cd hot-reload-engine
go build -o ./bin/hotreload ./cmd/hotreload

Or install directly:

go install github.com/Shiva210Jyoti/hot-reload-engine/cmd/hotreload@latest

Basic Usage

hotreload \
  --root ./myproject \
  --build "go build -o ./bin/server ./cmd/server" \
  --exec "./bin/server"

With a Config File

Create .hotreload.yaml in your project root:

root: ./myproject
build: go build -o ./bin/server ./cmd/server
exec: ./bin/server

debounce: 300ms

ignore_patterns:
  - "**/*_test.go"
  - "**/vendor/**"

health_check:
  enabled: true
  url: http://localhost:8080/health
  timeout: 5s
  interval: 500ms
  max_retries: 10

notifications:
  enabled: true

dashboard:
  enabled: true
  port: 3000

Then simply run:

hotreload

Try the Demo

make demo
# or
./bin/hotreload --root ./testserver \
  --build "go build -o ./bin/testserver ./testserver" \
  --exec "./bin/testserver"

In another terminal:

curl http://localhost:8080/       # → Server running - version 1.0.0
curl http://localhost:8080/health # → {"status":"ok", ...}
# Edit testserver/handler.go → save → watch auto-rebuild happen in < 2 seconds!

🏗️ Architecture

flowchart TD
    FS["📁 File System\n(fsnotify)"]

    subgraph Coordinator["🎯 Coordinator — Event Orchestrator"]
        direction TB
        W["🔍 Watcher\nRecursive fsnotify\n+ Smart Filter"]
        DB["⏱ Debouncer\n300ms collapse"]
        FP["🧠 Fingerprinter\nSHA256 skip-opt"]
        B["🔨 Builder\nexec + cancel"]
        HC["🏥 HealthChecker\nHTTP poll"]
        R["▶ Runner\nProcess manager\nSIGTERM→SIGKILL"]
        CT["🛡 CrashTracker\nExp. backoff"]
        N["🔔 Notifier\nDesktop alerts"]
        DASH["🌐 Dashboard\nWebSocket UI"]
    end

    FS -->|"OS events"| W
    W -->|"FileEvent"| DB
    DB -->|"one signal"| FP
    FP -->|"changed?"| B
    B -->|"success"| R
    B -->|"failure"| N
    R -->|"started"| HC
    R -->|"crash"| CT
    CT -->|"backoff"| R
    HC -->|"healthy"| N
    B -->|"events"| DASH
    R -->|"events"| DASH
Loading

See docs/ARCHITECTURE.md for a deep dive into every design decision.


📦 Package Layout

hot-reload-engine/
├── cmd/hotreload/             # CLI entry point — flag parsing → Coordinator
├── internal/
│   ├── config/                # Config struct, YAML loader, defaults, validation
│   ├── debouncer/             # Thread-safe time-based event debouncer
│   ├── watcher/               # Recursive fsnotify wrapper + smart filter
│   ├── builder/               # Build command executor with context cancellation
│   │   └── fingerprint.go     # SHA256 content hashing to skip redundant builds
│   ├── runner/                # Child process manager
│   │   ├── crashloop.go       # Exponential backoff crash detector
│   │   ├── process_unix.go    # SIGTERM → SIGKILL to process group
│   │   └── process_windows.go # taskkill /F /T /PID
│   ├── coordinator/           # Central event orchestrator
│   ├── dashboard/             # WebSocket HTTP server + embedded HTML UI
│   ├── healthcheck/           # HTTP health probe with retry logic
│   ├── logger/                # Custom slog handler with component labels
│   └── notifications/         # Cross-platform desktop notification (beeep)
├── testserver/                # Sample HTTP server for demonstration
│   ├── main.go                # Server bootstrap + graceful shutdown
│   └── handler.go             # /, /health, /crash endpoints
├── docs/
│   ├── ARCHITECTURE.md        # Deep-dive design patterns
│   ├── CONFIGURATION.md       # Complete config file reference
│   └── TROUBLESHOOTING.md     # Common issues and solutions
├── .hotreload.yaml            # Example config for the testserver
├── Makefile
├── go.mod
└── README.md

⚙️ CLI Flags

hotreload [flags]

Required (or set via .hotreload.yaml):
  --root string          Project root directory to watch
  --build string         Build command  e.g. "go build -o ./bin/app ."
  --exec string          Execute command  e.g. "./bin/app"

Optional:
  --debounce duration    Quiet period before rebuild (default 300ms)
  --ext string           Comma-separated extensions to watch (default ".go")
  --exclude string       Comma-separated extra glob patterns to exclude
  --notifications        Enable desktop notifications (default true)
  --dashboard            Enable web dashboard on :3000
  --dashboard-port int   Dashboard port (default 3000)
  --version              Print version and exit

Environment Variables

Variable Effect
HOTRELOAD_LOG_LEVEL Set log level: DEBUG, INFO (default), WARN, ERROR

📊 Performance

Metric Value
Typical rebuild time < 2 seconds
Baseline memory usage < 50 MB
Max watched files 10,000+
CPU overhead (idle) < 1%

Benchmarks

BenchmarkDebouncer-8         1000000      1043 ns/op
BenchmarkFingerprinter-8        5000    245631 ns/op
BenchmarkWatcher-8             10000    112458 ns/op

Run yourself:

go test -bench=. -benchmem ./...

🧪 Testing

# Run all tests
go test -v ./...

# Run with race detector
go test -v -race ./...

# Generate coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html

# View function-level summary
go tool cover -func=coverage.out

Current coverage: ~85%

Test Coverage Map

Package Scenarios
debouncer Single trigger, rapid collapse → 1 call, spaced triggers, Stop, Benchmark
watcher Single file, nested dirs, runtime-created dirs, deletions, ignore globs, shutdown, symlinks, Benchmark
filter .gitignore patterns, custom globs, defaults, table-driven edge cases
builder Success, failure, context cancel, real-time log streaming
fingerprint Same content, changed content, new file, non-Go files, Benchmark
runner Start/Stop, graceful/force shutdown, restart sequence, log streaming
crashloop Single backoff, exponential levels, reset
config File loading, CLI override, defaults, invalid YAML
coordinator Full E2E: build → binary → file change → rebuild → shutdown

📖 Design Decisions

Why Debouncing?

Editors often trigger multiple filesystem events per save. Without debouncing you'd get 5–10 redundant builds per keystroke. The 300ms default is tuned for typical editor behavior.

Why Build Fingerprinting?

git checkout, go mod tidy, and many SCM operations change file modification times without touching content. SHA256 fingerprinting ensures rebuilds only happen from real changes.

Why Process Groups?

A typical Go server spawns goroutines that open sockets and databases. On Unix, setting Setpgid: true lets us kill the entire process tree with a single kill(-pgid). Windows uses taskkill /F /T.

Why HTTP Health Checks?

Process started ≠ server ready. A port might not be bound for 300ms after launch. Health checks poll a configured URL and only mark the server as ready after it responds with 2xx.

Why Exponential Backoff?

Rapid crash-restart loops burn CPU, make debugging harder, and are hard on OS process tables. Backoff starts at 1s and doubles (capped at 32s) — giving you time to fix the root cause.

Why WebSockets for the Dashboard?

HTTP polling creates unnecessary overhead and adds latency. A single persistent WebSocket connection lets the server push events (build start, log lines, file changes) at zero overhead.


🔧 Makefile

make build    # Build the hotreload binary → ./bin/hotreload
make demo     # Watch the testserver with auto-reload
make test     # Run the full test suite
make bench    # Run benchmark suite
make clean    # Remove build artifacts

📄 Additional Documentation


📄 License

MIT License — see LICENSE


Built with ❤️ for the Go developer community

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors