reloader

package module
v0.0.0-...-4a7e5b3 Latest Latest
Warning

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

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

README

go-reloader

go-reloader is a simple implementation of Go live-reloading for development. It works by running a process that watches for changes in specified files and directories, and rebuilds + restarts the process when a change is detected.

Unlike other live-reload implementations, go-reloader is configured and run in your application so that custom loggers, build implementations, file watchers, and other aspects of the live-reload process can be customized.

go-reloader is still a WIP but is usable in its current state. Improvements and contributions are welcome!

Usage

The example below shows how to use go-reloader to watch for changes in the internal, pkg, and assets directories, and restart the application when a change is detected. It uses a simple HTTP health check to determine when the application is ready to receive requests to prevent the proxy from sending requests to the application before it's ready.

l := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
	Level: slog.LevelDebug,
}))

watcher, err := reloader.NewWatcher(
	"internal",
	"pkg",
	"assets",
)

if err != nil {
	panic(err)
}

appReloader := reloader.New(
	reloader.NewBuilder(
		[]string{"go", "build", "-o", "buzzword", "./cmd/app"},
		[]string{"./buzzword", "serve"},
		l,
		func() bool {
			res, err := http.Get("http://localhost:8080/_status")
			if err != nil {
				return false
			}

			if res.StatusCode != 200 {
				return false
			}

			return true
		},
	),
	watcher,
	l,
)

go func() {
	err := appReloader.ListenAndProxy(ctx, ":9000", ":8080")
	if err != nil && err != context.Canceled {
		panic(err)
	}
}()

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrAlreadyRunning = errors.New("already running")

Functions

This section is empty.

Types

type Application

type Application struct {
	// contains filtered or unexported fields
}

Application represents a running application that will be rebuilt and restarted as files change.

func New

func New(builder Builder, watcher Watcher, logger *slog.Logger) *Application

New creates a new Application with the given builder and watcher. The default target port is ":3000" but can be overridden via `WithTargetAddr`.

func (*Application) ListenAndProxy

func (app *Application) ListenAndProxy(ctx context.Context, addr string, targetAddr string) error

Start starts the application and listens for changes, rebuilding and restarting the target application in the background.

func (*Application) Started

func (app *Application) Started() <-chan struct{}

Started returns a channel that will be closed when the application has started.

type BasicBuilder

type BasicBuilder struct {
	// Bacoff is the function used to calculate the backoff duration between
	// attempts to start the application. It is called with the number of
	// attempts so far and should return the number of milliseconds to wait
	// before the next attempt.
	Backoff func(attempts int) int
	// contains filtered or unexported fields
}

func NewBuilder

func NewBuilder(buildCmd, runCmd []string, logger *slog.Logger, healthCheck func() bool) *BasicBuilder

NewBuilder returns a new builder that runs the given build and run commands.

func (*BasicBuilder) Build

func (b *BasicBuilder) Build() error

Build builds the application and blocks until the build is complete.

func (*BasicBuilder) ErrorText

func (b *BasicBuilder) ErrorText() string

ErrorText returns the last error text from the build or run commands.

func (*BasicBuilder) Run

func (b *BasicBuilder) Run() error

Run runs the application and returns an error if the application can't start. The function will not return until the application has started and passed the health check.

func (*BasicBuilder) Running

func (b *BasicBuilder) Running() bool

Running returns true if the application is currently running. Running should only be called when holding the lock.

func (*BasicBuilder) Stop

func (b *BasicBuilder) Stop()

Stop should stop the application if it is running. Stop blocks until the application has stopped.

func (*BasicBuilder) WithLock

func (b *BasicBuilder) WithLock(fn func())

WithLock runs the given function while holding a lock so the builder won't restart while the function is running. An sync.RWMutex is used under-the-hood so multiple functions can run at the same time.

type Builder

type Builder interface {
	// Build builds the application and blocks until the build is complete.
	Build() error
	// Run runs the application and returns an error if the application can't
	// start. This should not block.
	Run() error
	// Stop should stop the application if it is running. Stop should block until
	// the application has stopped.
	Stop()
	// Running returns true if the application is currently running.
	Running() bool
	// ErrorText returns the error text from the last build or run so it can be
	// displayed to the user.
	ErrorText() string
	// WithLock runs the given function while holding a lock so the builder
	// won't stop when the function is running.
	WithLock(func())
}

type Watcher

type Watcher interface {
	// Watch starts watching for changes to the application.
	Watch(ctx context.Context, events chan<- string) error
}

func NewWatcher

func NewWatcher(dirs ...string) (Watcher, error)

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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