Skip to content

bokwoon95/wgo

Repository files navigation

tests Go Report Card Coverage Status

wgo – watcher-go

Live reload for Go apps (and more)


Installation

You can download binaries from the release page, or use the Go command:

go install github.com/bokwoon95/wgo@latest
Usage:
  wgo [FLAGS] <command> [ARGUMENTS...]
  wgo gcc -o main main.c
  wgo go build -o main main.go
  wgo -file .c gcc -o main main.c
  wgo -file=.go go build -o main main.go

  wgo run [FLAGS] [GO_BUILD_FLAGS] <package> [ARGUMENTS...]
  wgo run main.go
  wgo run -file .html main.go arg1 arg2 arg3
  wgo run -file .html . arg1 arg2 arg3
  wgo run -file=.css -file=.js -tags=fts5 ./cmd/my_project arg1 arg2 arg3

Pass in the -h flag to the wgo/wgo run to learn what flags there are i.e. wgo -h, wgo run -h

Core documentation resides at https://github.com/bokwoon95/wgo#quickstart

Why this exists

Too many file watchers either force you to wrap your commands into strings, require config files or log tons of noisy output to your stdout. In contrast, wgo is dead simple and silent by default. The implementation is also really short, most of it resides in just two files (wgo_cmd.go and main.go). You can read the entire codebase in one sitting, start here.

It can be used like go run.

Quickstart

You already know how to use wgo. Simply slap wgo in front of any command in order to have it rerun whenever a file changes.

# Run gcc.
$ gcc -Wall -o main main.c

# Run gcc whenever a file changes.
$ wgo gcc -Wall -o main main.c

wgo run

wgo run behaves exactly like go run except it runs again the moment any Go file changes. This can be used to live-reload Go servers. wgo run accepts the same flags as go run.

By default wgo run only watches .go files. To include additional file types such as .html, use the -file flag.

# Run main.go.
$ go run main.go

# Run main.go whenever a .go file changes.
$ wgo run main.go

# Any flag that can be passed to `go run` can also be passed to `wgo run`.
$ wgo run -tags=fts5 -race -trimpath main.go

Flags

wgo/wgo run take in additional flags. These flags must be passed in directly after wgo/wgo run, before invoking your command.

  • -file/-xfile - Include/exclude files.
  • -dir/-xdir - Include/exclude directories.
  • -cd - Change to a different directory to run the commands.
  • -root - Specify additional root directories to watch.
  • -exit - Exit when the last command exits.
  • -stdin - Enable stdin for the last command.
  • -verbose - Log file events.

Including and excluding files

back to flags index

To include specific files eligible for triggering a reload, use the -file flag. It takes in a regex, and files whose paths (relative to the root directory) match the regex are included. You can provide multiple -file flags.

Path separators are always forward slash, even on Windows.

If no -file flag is provided, every file is included by default unless it is explicitly excluded by the -xfile flag.

# Run sass whenever an .scss file changes.
$ wgo -file .scss sass assets/styles.scss assets/styles.css

# Run main.go whenever a .go or .html or .css file changes.
$ wgo run -file .html -file .css main.go

# Run main.go when foo/bar/baz.go changes.
$ wgo run -file '^foo/bar/baz.go$' main.go

To exclude specific files, use the -xfile flag. It takes in a regex (like -file) but if it matches with a file path that file is excluded.

The -xfile flag takes higher precedence than the -file flag so you can include a large group of files using a -file flag and surgically ignore specific files from that group using an -xfile flag.

# `go test` writes to coverage.out, which counts as a changed file which triggers
# `go test` to run again which generates coverage.out again in an infinite loop.
# Avoid this by excluding any file matching 'coverage.out'.
$ wgo -xfile coverage.out go test . -race -coverprofile=coverage.out

# Probably better to specify `-file .go` in order to rerun `go test` only when
# .go files change.
$ wgo -file .go go test . -race -coverprofile=coverage.out

Regex dot literals

The -file flag takes in regexes like .html or .css.

$ wgo run -file .html -file .css main.go

Technically the dot . matches any character, but file extensions are such a common pattern that wgo includes a slight modification to the regex matching rules:

Any dot . immediately followed by an alphabet [a-zA-Z] is treated as a dot literal i.e. \.

So .css really means \.css, but .* still means .* because * is not an alphabet. If you really want to use the dot . wildcard followed by an alphabet, wrap it in (a bracket group) so that it is not immediately followed by an alphabet i.e. (.)abc.

Including and excluding directories

back to flags index

To only watch specific directories, use the -dir flag. It takes in a regex, and directories whose paths (relative to the root directory) match the regex are included. You can provide multiple -dir flags.

Path separators are always forward slash, even on Windows.

# Run sass whenever an .scss file in the assets directory changes.
$ wgo -dir assets -file .scss sass assets/styles.scss assets/styles.css

# Run main.go whenever something in the foo directory or bar/baz directory changes.
$ wgo run -dir foo -dir bar/baz main.go

# Run main.go whenever something in the foo/bar/baz directory changes.
$ wgo run -dir '^foo/bar/baz$' main.go

To exclude specific directories, use the -xdir flag. It takes in a regex (like -dir) but if it matches with a directory path that directory is excluded from being watched.

# Run main.go whenever a file changes, ignoring any directory called node_modules.
$ wgo run -xdir node_modules main.go

In practice you don't have to exclude node_modules because it's already excluded by default (together with .git, .hg, .svn, .idea, .vscode and .settings). If you do want to watch any of those directories, you should explicitly include it with the -dir flag.

Chaining commands

Commands can be chained using the :: separator. Subsequent commands are executed only when the previous command succeeds.

# Run `make build` followed by `make run` whenever a file changes.
$ wgo make build :: make run

# Run `go build` followed by the built binary whenever a .go file changes.
$ wgo -file .go go build -o main main.go :: ./main

# Clear the screen with `clear` before running `go test`.
# Windows users should use `cls` instead of `clear`.
$ wgo -file .go clear :: go test . -race -coverprofile=coverage.out

Escaping the command separator

Since :: designates the command separator, if you actually need to pass in a :: string an an argument to a command you should escape it by appending an extra : to it. So :: is escaped to :::, ::: is escaped to ::::, and so on.

Shell wrapping

Chained commands execute in their own independent environment and are not implicitly wrapped in a shell. This can be a problem if you want to use shell specific commands like if-else statements or if you want to pass data between commands. In this case you should explicitly wrap the command(s) in a shell using the form sh -c '<command>' (or pwsh.exe -command '<command>' if you're on Windows).

# bash: echo "passed" or "failed" depending on whether the program succeeds.
$ wgo -file .go go build -o main main.go :: bash -c 'if ./main; then echo "passed"; else echo "failed"; fi'

# powershell: echo "passed" or "failed" depending on whether the program succeeds.
$ wgo -file .go go build -o main main.go :: pwsh.exe -command './main; if ($LastExitCode -eq 0) { echo "passed" } else { echo "failed" }'

Running parallel wgo commands

If a command separator :: is followed by wgo, a new wgo command is started (which runs in parallel).

# Run the echo commands sequentially.
$ wgo echo foo :: echo bar :: echo baz

# Run the echo commands in parallel.
$ wgo echo foo :: wgo echo bar :: wgo echo baz

This allows you to reload a server when .go files change, but also do things like rebuild .scss and .ts files whenever they change at the same time.

$ wgo run main.go \
    :: wgo -file .scss sass assets/styles.scss assets/styles.css \
    :: wgo -file .ts tsc 'assets/*.ts' --outfile assets/index.js

Running commands in a different directory

back to flags index

If you want to run commands in a different directory from where wgo was invoked, used the -cd flag.

# Run main.go whenever a file changes.
$ wgo run main.go

# Run main.go from the 'app' directory whenever a file changes.
$ wgo run -cd app main.go

Specify additional root directories to watch

back to flags index

By default, the root being watched is the current directory. You can watch additional roots with the -root flag. Note that the -file/-xfile and -dir/-xdir filters apply equally across all roots.

# Run main.go whenever a file in the current directory or the parent directory changes.
$ wgo run -root .. main.go

# Run main.go whenever a file in the current directory or the /env_secrets directory changes.
$ wgo run -root /env_secrets main.go

You may also be interested in the -cd flag, which lets you watch a directory but run commands from a different directory.

Exit when the last command exits

back to flags index

If the -exit flag is provided, wgo exits once the last command exits. This is useful if your program is something like a server which should block forever, so file changes can trigger a reload as usual but if the server ever dies on its own (e.g. a panic or log.Fatal) wgo should exit to signal to some supervisor process that the server has been terminated.

# Exit once main.go exits.
$ wgo run -exit main.go

# Exit once ./main exits.
$ wgo -exit -file .go go build -o main main.go :: ./main

Enable stdin

back to flags index

By default, stdin to wgo is ignored. You can enable it for the last command using the -stdin flag. This is useful for reloading programs that read from stdin.

# Allow main.go to read from stdin.
$ wgo run -stdin main.go

# Only ./main (the last command) gets to read from stdin, `go build` does not get stdin.
$ wgo -stdin -file .go go build -o main main.go :: ./main

Log file events

back to flags index

If the -verbose flag is provided, file events are logged.

Without -verbose:

$ wgo run -verbose ./server
Listening on localhost:8080
Listening on localhost:8080 # <-- file edited.

With -verbose:

$ wgo run -verbose ./server
[wgo] WATCH /Users/bokwoon/Documents/wgo/testdata
[wgo] WATCH args
[wgo] WATCH build_flags
[wgo] WATCH dir
[wgo] WATCH dir/foo
[wgo] WATCH dir/subdir
[wgo] WATCH dir/subdir/foo
[wgo] WATCH env
[wgo] WATCH file_event
[wgo] WATCH hello_world
[wgo] WATCH server
[wgo] WATCH signal
[wgo] WATCH stdin
Listening on localhost:8080
[wgo] CREATE server/main.go~
[wgo] CREATE server/main.go
[wgo] WRITE server/main.go
Listening on localhost:8080

Why should I use this over other file watchers?

Nothing! File watchers honestly all do the same things, if you find a file watcher that works for you there's no reason to change. Maybe wgo has a lower bar to entry? Or maybe you're allergic to unnecessary config files like I am. Or if you want a feature like chained commands or parallel commands, consider using wgo.

Contributing

See START_HERE.md.

One-liner to download the latest release

Linux

curl --location --output wgo "$(curl 'https://api.github.com/repos/bokwoon95/wgo/releases/latest' | grep '"browser_download_url": ".*wgo-linux"' | xargs | cut -d ' ' -f 2)"

Linux (ARM)

curl --location --output wgo "$(curl 'https://api.github.com/repos/bokwoon95/wgo/releases/latest' | grep '"browser_download_url": ".*wgo-linux-arm"' | xargs | cut -d ' ' -f 2)"

macOS

curl --location --output wgo "$(curl 'https://api.github.com/repos/bokwoon95/wgo/releases/latest' | grep '"browser_download_url": ".*wgo-macos"' | xargs | cut -d ' ' -f 2)"

macOS (Apple Silicon)

curl --location --output wgo "$(curl 'https://api.github.com/repos/bokwoon95/wgo/releases/latest' | grep '"browser_download_url": ".*wgo-macos-apple-silicon"' | xargs | cut -d ' ' -f 2)"

Windows

Invoke-WebRequest -OutFile wgo.exe -Uri ((Invoke-RestMethod -Uri 'https://api.github.com/repos/bokwoon95/wgo/releases/latest').assets | Where-Object { $_.name -eq 'wgo-windows.exe' }).browser_download_url