hotato

command module
v0.0.31 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 21, 2023 License: MIT Imports: 0 Imported by: 0

README

🚧 EARLY DEVELOPMENT 🚧

This is a small tool I have built that is largely untested off of my machine. You are welcome to try it and if you notice any issues report them on the github and I will look into them.

HOTATO Hot Reload

Hotato (hot potato) is CLI tool for hot reloading your codebase based on file system changes using notify with the ablity to use as a golang library in your own projects.

Key Features

  • Based on Notify to allievate common problems with popular FS libraries on mac that open a listener per file by using apples built-in FSEvents.
  • Allows for customization via code / config file / cli flags
  • Extended customization when used as a library using reloadCallback to bypass hotato rulesets and add addtional logic/logging on your applications end
  • Default slogger built in with the ablity to mute logs as well as pass in your own slog handler to be used in app
  • MIT licensed

Install

Installing via go CLI is the easiest method more methods are on the list

go install github.com/atterpac/hotato/cmd/hotato@latest

Alternative if you wish to use as a package and not a cli

go get github.com/atterpac/hotato

Usage

Flags

-p Root path that will be watched and commands will be executed in typically this is './'

-be Command to be called before the exec command for example go mod tidy

-e Command to be called when a modification is detected for example go run main.go

-ae Command to b be called when a modifcation is detected after the main process closes

-l Log Level to display options can include "debug", "info","warn","error"

-f path to a TOML config file see Config File for details on the format of config

-id Ignore directories provided as a comma-separated list

-if Ignore files provided as a comma-separated list

-ie Ignore extensions provided as a comma-separated list

-d Debounce timer in milliseconds, used to ignore repetitive system

Example
hotato -p ./ -e "go run main.go" -be "go mod tidy" -ae "rm ./main" -l "debug" -id ".git, node_modules" -if ".env" -ie ".db, .sqlite" -d 500

Embedding into your dev project

There can be some uses where you might want to start a watcher internally or for a tool for development Hotato provides a function NewEngineFromOptions which takes an hotato.Config and allows for the engine.Start() function

Using hotato as a library also opens the ability to add a Callback Callback function that is called on every FS notification

Structs
type Config struct {
	RootPath     string `toml:"root_path"`
	PreExec      string `toml:"pre_exec"`
	ExecCommand  string `toml:"exec_command"`
	PostExec     string `toml:"post_exec"`
	Ignore       Ignore `toml:"ignore"`
	LogLevel     string `toml:"log_level"`
	Debounce     int    `toml:"debounce"`
	Slog         *slog.Logger
	ExternalSlog bool
}

type Ignore struct {
	Dir       map[string]bool `toml:"dir"`
	File      map[string]bool `toml:"file"`
	Extension map[string]bool `toml:"extension"`
}
Example
import ( // other imports
    "github.com/atterpac/hotato/engine"
    )

func main () {
	ignore := hotato.Ignore{
		File:      map[string]bool{{"ignore.go",true},{".env", true}},
		Dir:       map[string]bool{{".git",true},{"node_modules", true}},
		Extension: map[string]bool{{".txt",true},{".db", true}},
	}
	config := hotato.Config{
		RootPath:    "./subExecProcess",
		ExecCommand: "go run main.go",
		LogLevel:    "info", // debug | info | warn | error | mute (discards all logs)
		Ignore:      ignore,
		Debounce:    1000,
		Slog: nil, // Optionally provide a slog interface
                  // if nil a default will be provided
                  // If provided stdout will not be piped through gotato

		// Optionally provide a callback function to be called upon file notification events
                Callback: func(*EventCallback) EventHandle 
	}
	engine := hotato.NewEngineFromConfig(config)
	engine.Start()

	// Stop monitoring files and kill child processes
	engine.Stop()
}
Reload Callback
Event Types

The following are all the file system event types that can be passed into the callback functions. Important to note that some actions only are emitted are certain OSs and you may have to handle those if you wish to bypass hotato rulesets

const (
    // Base Actions
	Create Event = iota
	Write
	Remove
	Rename
	// Windows Specific Actions
	ActionModified
	ActionRenamedNewName
	ActionRenamedOldName
	ActionAdded
	ActionRemoved
	ChangeLastWrite
	ChangeAttributes
	ChangeSize
	ChangeDirName
	ChangeFileName
	ChangeSecurity
	ChangeCreation
	ChangeLastAccess
	// Linux Specific Actions
	InCloseWrite
	InModify
	InMovedTo
	InMovedFrom
	InCreate
	InDelete
)

// Used as a response to the Callback 
const (
	EventContinue EventHandle = iota
	EventBypass
	EventIgnore
)
Callback Function

Below describes the data that you recieve in the callback function as well as an example of how this could be used.

Callbacks should return an hotato.EventHandle

hotato.EventContinue continues with the reload process as normal and follows the hotato ruleset defined in the config

hotato.EventBypass disregards all config rulesets and restarts the exec process

hotato.EventIgnore ignores the event and continues monitoring

// Called whenever a change is detected in the filesystem
// By default we ignore file rename/remove and a bunch of other events that would likely cause breaking changes on a reload  see eventmap_[oos].go for default rules
type EventCallback struct {
	Type Event  // Type of Notification (Write/Create/Remove...)
	Time time.Time // time.Now() when event was triggered
	Path string    // Full path to the modified file
}
// Available returns from the Callback function
const (
	EventContinue EventHandle = iota
	EventBypass
	EventIgnore
)

func ExampleCallback(e hotato.EventCallback) hotato.EventHandle {
	switch e.Type {
	case hotato.Create:
		// Continue with reload process based on configured ruleset
		return hotato.EventContinue
	case hotato.Write:
		// Ignore a file that would normally trigger a reload based on config
		if e.Path == "./path/to/watched/file" {
			return hotato.EventIgnore
		}
		// Continue with reload ruleset but add some extra logs/logic
		fmt.Println("File Modified: %s", e.Path)	
		return EventContinue
	case hotato.Remove:
		// Hotato will ignore this event by default
		// Return EventBypass to force reload process
		return hotato.EventBypass
	}
	return hotato.EventContinue
}
Config File

If you would prefer to load from a config file rather than building the structs you can use

hotato.NewEngineFromTOML("path/to/toml")
Example Config
[config]
# Relative to this files location
root_path = "./"
# Runs prior to the exec command starting
pre_exec = "go mod tidy"
# Command to run on reload
exec_command = "go run main.go"
# Runs when a file reload is triggered after killing the previous process
post_exec = ""
# debug | info | warn | error | mute
# Defaults to Info if not provided
log_level = "info" 
# Debounce setting for ignoring reptitive file system notifications
debounce = 1000 # Milliseconds
# Sets what files the watcher should ignore
[config.ignore]
# Directories to ignore
dir = [".git", "node_modules", "newdir"]
# Files to ignore
file = [".DS_Store", ".gitignore", ".gitkeep", "newfile.go"]
# File extensions to ignore
extension = [".db", ".sqlite"]
Alternatives

Hotato not for you? Here are some popular hot reload alternatives

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL