Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the bash script a bit less clumsy on close. #4

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 17 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,36 @@
go-reload
=========

This is a Bash script for automatically reloading Go programs. It acts as a wrapper for `go run`, stopping and restarting the process whenever a `.go` file in your current directory or `$GOPATH/src` folder is saved.
This is a Bash script for automatically reloading Go programs. It acts as a
wrapper for `go run`, stopping and restarting the process whenever a `.go` file
in your current directory or `$GOPATH/src` folder is saved.

It comes in useful when developing Go web applications.

Installation
------------

1) Install [inotify-tools](https://github.com/rvoicilas/inotify-tools) and clone this repository:
1) Install [inotify-tools](https://github.com/rvoicilas/inotify-tools) and clone
this repository:

```
$ sudo apt-get install inotify-tools
$ git clone https://github.com/alexedwards/go-reload.git
```
$ sudo apt-get install inotify-tools
$ git clone https://github.com/alexedwards/go-reload.git

2) Make the script executable and move it to somewhere on your system path. For example:
2) Make the script executable and move it to somewhere on your system path. For
example:

```
$ cd go-reload
$ chmod +x go-reload
$ sudo mv go-reload /usr/local/bin/
```
$ cd go-reload
$ chmod +x go-reload
$ sudo mv go-reload /usr/local/bin/

Usage
-----

Use the script in place of `go run`. For example:

```
$ go-reload main.go
== Go-reload
>> Watching directories, CTRL+C to stop
```
$ go-reload main.go
== Go-reload
>> Watching directories, CTRL+C to stop

**Sublime Text 3 users**: For this script to work you must disable atomic saving in your preferences: `"atomic_save": false`
**Sublime Text 3 users**: For this script to work you must disable atomic saving
in your preferences: `"atomic_save": false`
191 changes: 153 additions & 38 deletions go-reload
Original file line number Diff line number Diff line change
@@ -1,56 +1,171 @@
#!/bin/bash
#
# Watch for go changes and then restart the go runner.
#
# This acts as a wrapper for go run, stopping and restarting the processes
# whenever a .go file in the $GOPATH changes. Very handy for go web
# applications.
#
# Just use this script in place of go run. For example:
# : $ go-reload main.go
# : == Go-reload
# : >> Watching directories, CTRL+c to stop
#
# To use this, you will need inotify-tools, go (duh), and your $GOPATH should be
# properly set!

# Watch all *.go files in the specified directory
# Call the restart function when they are saved
set -e

declare -r FILE_PATH=$1
declare -r PID=$$

shift
declare -r ARGS=$@

################################################################################
# Watches all *.go files in the specific directory and calls `restart' when
# those files are updated.
#
# Globals:
# None
# Arguments:
# The path to monitor.
# Returns:
# None
################################################################################
function monitor() {
inotifywait -q -m -r -e close_write --exclude '[^g][^o]$' $1 |
local path=$1
local pipe=<(inotifywait --quiet --monitor --recursive --event close_write \
--exclude '[^g][^o]$' $path)
while read line; do
restart
done
done < $pipe
}

# Terminate and rerun the main Go program
################################################################################
# Terminates and re runs the main Go program.
#
# Globals:
# None
# Arguments:
# None
# Returns:
# None
################################################################################
function restart {
if [ "$(pidof $PROCESS_NAME)" ]; then
killall -q -w -9 $PROCESS_NAME
fi
close
echo ">> Reloading..."
start
}

################################################################################
# Starts the main Go program.
#
# Globals:
# FILE_PATH
# ARGS
# Arguments:
# None
# Returns:
# None
################################################################################
function start {
local pids=$(pidof go run $FILE_PATH)
if [[ -n $pids ]]; then
echo "The go app is already running!" >&2
exit 1
fi
go run $FILE_PATH $ARGS &
}

# Make sure all background processes get terminated
################################################################################
# Terminates the main Go program
#
# Globals:
# FILE_PATH
# Arguments:
# None
# Returns:
# None
################################################################################
function close {
killall -q -w -9 inotifywait
# TODO: Getting the pids of all "go run main.go" commands is still a little
# weak. See about keeping track of the pids in a variable.
local pids=$(pidof go run $FILE_PATH)
if [[ -n $pids ]]; then
for p in $pids; do
# Kill the child processes of go run, namely, the app that's running.
# TODO: Nuke these if asking nicely doesn't work!
ps --no-headers -o pid --ppid=$p | xargs kill
kill $p
done
fi
}

################################################################################
# Cleans up and stops all running processes.
#
# Globals:
# PID
# Arguments:
# None
# Returns:
# None
################################################################################
function cleanup {
echo "Clean it up"
# Kill all of the child processes so that we don't have hanging inotifywait
# calls.
kill -- -$PID
exit 0
}

trap close INT
echo "== Go-reload"
echo ">> Watching directories, CTRL+C to stop"
################################################################################
# Runs the core logic of the app.
#
# Globals:
# GOPATH
# Arguments:
# None
# Returns:
# None
################################################################################
function main {
echo "== Go-reload"
echo ">> Watching directories, CTRL+C to stop"

FILE_PATH=$1
FILE_NAME=$(basename $FILE_PATH)
PROCESS_NAME=${FILE_NAME%%.*}
start

shift
ARGS=$@

# Start the main Go program
go run $FILE_PATH $ARGS &

# Monitor all /src directories on the GOPATH
OIFS="$IFS"
IFS=':'
for path in $GOPATH
do
monitor $path/src &
done
IFS="$OIFS"

# If the current working directory isn't on the GOPATH, monitor it too
if [[ $PWD != $GOPATH* ]]
then
monitor $PWD
fi

wait
# Monitor the /src directories in all directories on the GOPATH. If the
# current working directory isn't in the GOPATH, then monitor that one as
# well.

local app_path=$(realpath .)
local source_in_path=false

local oifs="${IFS}"
IFS=":"
for path in $GOPATH; do
monitor $path/src &
if [[ $app_path == $path* ]]; then
source_in_path=true
fi
done
IFS="${oifs}"

if [[ $source_in_path == false ]]; then
monitor $app_path &
fi

wait
}

trap cleanup INT

# And start.
main

# Local Variables:
# sh-basic-offset: 2
# fill-column: 80
# End: