diff --git a/.gitignore b/.gitignore
index 81ea26a..db83d95 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,9 @@
# binaries
*.exe
-gaper
+/gaper
srv
vendor
coverage.out
.DS_Store
+testdata/server/server
+dist
diff --git a/.goreleaser.yml b/.goreleaser.yml
index 647bf8c..5ce78e8 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -1,5 +1,5 @@
builds:
- - main: main.go
+ - main: ./cmd/gaper/main.go
binary: gaper
goos:
- windows
diff --git a/.travis.yml b/.travis.yml
index 4be5593..84f3531 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,10 +7,12 @@ go:
- 1.10.x
# - master
-script:
+before_script:
- go version
- make setup
- make lint
+
+script:
- make test
after_success:
diff --git a/Makefile b/Makefile
index e7bc5e8..a9933e9 100644
--- a/Makefile
+++ b/Makefile
@@ -19,7 +19,7 @@ ifndef LINTER
endif
build:
- @go build .
+ @go build -o ./gaper cmd/gaper/main.go
## lint: Validate golang code
lint:
@@ -30,7 +30,7 @@ lint:
--vendor ./...
test:
- @go test -v -coverpkg $(COVER_PACKAGES) \
+ @go test -p=1 -coverpkg $(COVER_PACKAGES) \
-covermode=atomic -coverprofile=coverage.out $(TEST_PACKAGES)
cover: test
diff --git a/README.md b/README.md
index 448fbfc..a1b227e 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,8 @@
gaper
- Restarts programs when they crash or a watched file changes.
+ Used to build and restart a Go project when it crashes or some watched file changes
+
Aimed to be used in development only.
@@ -20,7 +21,7 @@
## Installation
```
-go get -u github.com/maxcnunes/gaper
+go get -u github.com/maxcnunes/gaper/cmd/gaper
```
## Changelog
@@ -31,32 +32,34 @@ See [Releases](https://github.com/maxcnunes/gaper/releases) for detailed history
```
NAME:
- gaper - Used to restart programs when they crash or a watched file changes
+ gaper - Used to build and restart a Go project when it crashes or some watched file changes
USAGE:
gaper [global options] command [command options] [arguments...]
VERSION:
- 0.0.0
+ version
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
- --bin-name value name for the binary built by Gaper for the executed program
- --build-path value path to the program source code
- --build-args value build arguments passed to the program
- --verbose turns on the verbose messages from Gaper
- --watch value, -w value list of folders or files to watch for changes
- --ignore value, -i value list of folders or files to ignore for changes
- --poll-interval value, -p value how often in milliseconds to poll watched files for changes (default: 500)
- --extensions value, -e value a comma-delimited list of file extensions to watch for changes (default: "go")
- --no-restart-on value, -n value don't automatically restart the executed program if it ends:
- if "error", an exit code of 0 will still restart.
- if "exit", no restart regardless of exit code.
- if "success", no restart only if exit code is 0.
- --help, -h show help
- --version, -v print the version
+ --bin-name value name for the binary built by gaper for the executed program (default current directory name)
+ --build-path value path to the program source code (default: ".")
+ --build-args value arguments used on building the program
+ --program-args value arguments used on executing the program
+ --verbose turns on the verbose messages from gaper
+ --watch value, -w value list of folders or files to watch for changes
+ --ignore value, -i value list of folders or files to ignore for changes
+ (always ignores all hidden files and directories)
+ --poll-interval value, -p value how often in milliseconds to poll watched files for changes (default: 500)
+ --extensions value, -e value a comma-delimited list of file extensions to watch for changes (default: "go")
+ --no-restart-on value, -n value don't automatically restart the supervised program if it ends:
+ if "error", an exit code of 0 will still restart.
+ if "exit", no restart regardless of exit code.
+ if "success", no restart only if exit code is 0.
+ --help, -h show help
+ --version, -v print the version
```
### Examples
diff --git a/builder.go b/builder.go
index 28e1bbf..d46b27a 100644
--- a/builder.go
+++ b/builder.go
@@ -1,4 +1,4 @@
-package main
+package gaper
import (
"fmt"
diff --git a/builder_test.go b/builder_test.go
index a51e202..c7e6c5a 100644
--- a/builder_test.go
+++ b/builder_test.go
@@ -1,4 +1,4 @@
-package main
+package gaper
import (
"os"
diff --git a/cmd/gaper/main.go b/cmd/gaper/main.go
new file mode 100644
index 0000000..141f26e
--- /dev/null
+++ b/cmd/gaper/main.go
@@ -0,0 +1,107 @@
+package main
+
+import (
+ "os"
+
+ "github.com/maxcnunes/gaper"
+ "github.com/urfave/cli"
+)
+
+// build info
+var (
+ version = "dev"
+)
+
+var logger = gaper.NewLogger("gaper")
+
+func main() {
+ parseArgs := func(c *cli.Context) *gaper.Config {
+ return &gaper.Config{
+ BinName: c.String("bin-name"),
+ BuildPath: c.String("build-path"),
+ BuildArgsMerged: c.String("build-args"),
+ ProgramArgsMerged: c.String("program-args"),
+ Verbose: c.Bool("verbose"),
+ WatchItems: c.StringSlice("watch"),
+ IgnoreItems: c.StringSlice("ignore"),
+ PollInterval: c.Int("poll-interval"),
+ Extensions: c.StringSlice("extensions"),
+ NoRestartOn: c.String("no-restart-on"),
+ ExitOnSIGINT: true,
+ }
+ }
+
+ app := cli.NewApp()
+ app.Name = "gaper"
+ app.Usage = "Used to build and restart a Go project when it crashes or some watched file changes"
+ app.Version = version
+
+ app.Action = func(c *cli.Context) {
+ args := parseArgs(c)
+ if err := gaper.Run(args); err != nil {
+ logger.Error(err)
+ os.Exit(1)
+ }
+ }
+
+ exts := make(cli.StringSlice, len(gaper.DefaultExtensions))
+ for i := range gaper.DefaultExtensions {
+ exts[i] = gaper.DefaultExtensions[i]
+ }
+
+ // supported arguments
+ app.Flags = []cli.Flag{
+ cli.StringFlag{
+ Name: "bin-name",
+ Usage: "name for the binary built by gaper for the executed program (default current directory name)",
+ },
+ cli.StringFlag{
+ Name: "build-path",
+ Value: gaper.DefaultBuildPath,
+ Usage: "path to the program source code",
+ },
+ cli.StringFlag{
+ Name: "build-args",
+ Usage: "arguments used on building the program",
+ },
+ cli.StringFlag{
+ Name: "program-args",
+ Usage: "arguments used on executing the program",
+ },
+ cli.BoolFlag{
+ Name: "verbose",
+ Usage: "turns on the verbose messages from gaper",
+ },
+ cli.StringSliceFlag{
+ Name: "watch, w",
+ Usage: "list of folders or files to watch for changes",
+ },
+ cli.StringSliceFlag{
+ Name: "ignore, i",
+ Usage: "list of folders or files to ignore for changes\n" +
+ "\t\t(always ignores all hidden files and directories)",
+ },
+ cli.IntFlag{
+ Name: "poll-interval, p",
+ Value: gaper.DefaultPoolInterval,
+ Usage: "how often in milliseconds to poll watched files for changes",
+ },
+ cli.StringSliceFlag{
+ Name: "extensions, e",
+ Value: &exts,
+ Usage: "a comma-delimited list of file extensions to watch for changes",
+ },
+ cli.StringFlag{
+ Name: "no-restart-on, n",
+ Usage: "don't automatically restart the supervised program if it ends:\n" +
+ "\t\tif \"error\", an exit code of 0 will still restart.\n" +
+ "\t\tif \"exit\", no restart regardless of exit code.\n" +
+ "\t\tif \"success\", no restart only if exit code is 0.",
+ },
+ }
+
+ if err := app.Run(os.Args); err != nil {
+ logger.Errorf("Error running gaper: %v", err)
+ os.Exit(1)
+ }
+}
diff --git a/gaper.go b/gaper.go
new file mode 100644
index 0000000..e3b0a79
--- /dev/null
+++ b/gaper.go
@@ -0,0 +1,201 @@
+// Package gaper implements a supervisor restarts a go project
+// when it crashes or a watched file changes
+package gaper
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "os/signal"
+ "path/filepath"
+ "syscall"
+ "time"
+
+ shellwords "github.com/mattn/go-shellwords"
+)
+
+// DefaultBuildPath is the default build and watched path
+var DefaultBuildPath = "."
+
+// DefaultExtensions is the default watched extension
+var DefaultExtensions = []string{"go"}
+
+// DefaultPoolInterval is the time in ms used by the watcher to wait between scans
+var DefaultPoolInterval = 500
+
+var logger = NewLogger("gaper")
+
+// exit statuses
+var exitStatusSuccess = 0
+var exitStatusError = 1
+
+// Config contains all settings supported by gaper
+type Config struct {
+ BinName string
+ BuildPath string
+ BuildArgs []string
+ BuildArgsMerged string
+ ProgramArgs []string
+ ProgramArgsMerged string
+ WatchItems []string
+ IgnoreItems []string
+ PollInterval int
+ Extensions []string
+ NoRestartOn string
+ Verbose bool
+ ExitOnSIGINT bool
+}
+
+// Run in the gaper high level API
+// It starts the whole gaper process watching for file changes or exit codes
+// and restarting the program
+func Run(cfg *Config) error { // nolint: gocyclo
+ var err error
+ logger.Verbose(cfg.Verbose)
+ logger.Debug("Starting gaper")
+
+ if len(cfg.BuildPath) == 0 {
+ cfg.BuildPath = DefaultBuildPath
+ }
+
+ cfg.BuildArgs, err = parseInnerArgs(cfg.BuildArgs, cfg.BuildArgsMerged)
+ if err != nil {
+ return err
+ }
+
+ cfg.ProgramArgs, err = parseInnerArgs(cfg.ProgramArgs, cfg.ProgramArgsMerged)
+ if err != nil {
+ return err
+ }
+
+ wd, err := os.Getwd()
+ if err != nil {
+ return err
+ }
+
+ if len(cfg.WatchItems) == 0 {
+ cfg.WatchItems = append(cfg.WatchItems, cfg.BuildPath)
+ }
+
+ builder := NewBuilder(cfg.BuildPath, cfg.BinName, wd, cfg.BuildArgs)
+ runner := NewRunner(os.Stdout, os.Stderr, filepath.Join(wd, builder.Binary()), cfg.ProgramArgs)
+
+ if err = builder.Build(); err != nil {
+ return fmt.Errorf("build error: %v", err)
+ }
+
+ shutdown(runner, cfg.ExitOnSIGINT)
+
+ if _, err = runner.Run(); err != nil {
+ return fmt.Errorf("run error: %v", err)
+ }
+
+ watcher, err := NewWatcher(cfg.PollInterval, cfg.WatchItems, cfg.IgnoreItems, cfg.Extensions)
+ if err != nil {
+ return fmt.Errorf("watcher error: %v", err)
+ }
+
+ var changeRestart bool
+
+ go watcher.Watch()
+ for {
+ select {
+ case event := <-watcher.Events:
+ logger.Debug("Detected new changed file: ", event)
+ changeRestart = true
+ if err := restart(builder, runner); err != nil {
+ return err
+ }
+ case err := <-watcher.Errors:
+ return fmt.Errorf("error on watching files: %v", err)
+ case err := <-runner.Errors():
+ if changeRestart {
+ changeRestart = false
+ } else {
+ logger.Debug("Detected program exit: ", err)
+ if err = handleProgramExit(builder, runner, err, cfg.NoRestartOn); err != nil {
+ return err
+ }
+ }
+ default:
+ time.Sleep(time.Duration(cfg.PollInterval) * time.Millisecond)
+ }
+ }
+}
+
+func restart(builder Builder, runner Runner) error {
+ logger.Debug("Restarting program")
+
+ // kill process if it is running
+ if !runner.Exited() {
+ if err := runner.Kill(); err != nil {
+ return fmt.Errorf("kill error: %v", err)
+ }
+ }
+
+ if err := builder.Build(); err != nil {
+ return fmt.Errorf("build error: %v", err)
+ }
+
+ if _, err := runner.Run(); err != nil {
+ return fmt.Errorf("run error: %v", err)
+ }
+
+ return nil
+}
+
+func handleProgramExit(builder Builder, runner Runner, err error, noRestartOn string) error {
+ exiterr, ok := err.(*exec.ExitError)
+ if !ok {
+ return fmt.Errorf("couldn't handle program crash restart: %v", err)
+ }
+
+ status, oks := exiterr.Sys().(syscall.WaitStatus)
+ if !oks {
+ return fmt.Errorf("couldn't resolve exit status: %v", err)
+ }
+
+ exitStatus := status.ExitStatus()
+
+ // if "error", an exit code of 0 will still restart.
+ if noRestartOn == "error" && exitStatus == exitStatusError {
+ return nil
+ }
+
+ // if "success", no restart only if exit code is 0.
+ if noRestartOn == "success" && exitStatus == exitStatusSuccess {
+ return nil
+ }
+
+ // if "exit", no restart regardless of exit code.
+ if noRestartOn == "exit" {
+ return nil
+ }
+
+ return restart(builder, runner)
+}
+
+func shutdown(runner Runner, exitOnSIGINT bool) {
+ c := make(chan os.Signal, 2)
+ signal.Notify(c, os.Interrupt, syscall.SIGTERM)
+ go func() {
+ s := <-c
+ logger.Debug("Got signal: ", s)
+
+ if err := runner.Kill(); err != nil {
+ logger.Error("Error killing: ", err)
+ }
+
+ if exitOnSIGINT {
+ os.Exit(0)
+ }
+ }()
+}
+
+func parseInnerArgs(args []string, argsm string) ([]string, error) {
+ if len(args) > 0 || len(argsm) == 0 {
+ return args, nil
+ }
+
+ return shellwords.Parse(argsm)
+}
diff --git a/main_test.go b/gaper_test.go
similarity index 50%
rename from main_test.go
rename to gaper_test.go
index f0c3559..d30ae48 100644
--- a/main_test.go
+++ b/gaper_test.go
@@ -1,9 +1,9 @@
-package main
+package gaper
import (
"testing"
)
func TestGaper(t *testing.T) {
- // TODO: add test to main file
+ // TODO: add test to gaper high level API
}
diff --git a/loggger.go b/loggger.go
index 7029a88..4a8385a 100644
--- a/loggger.go
+++ b/loggger.go
@@ -1,4 +1,4 @@
-package main
+package gaper
import (
"log"
diff --git a/main.go b/main.go
deleted file mode 100644
index c21e8ec..0000000
--- a/main.go
+++ /dev/null
@@ -1,264 +0,0 @@
-// Package gaper implements a supervisor restarts a go project
-// when it crashes or a watched file changes
-package main
-
-import (
- "fmt"
- "os"
- "os/exec"
- "os/signal"
- "path/filepath"
- "syscall"
- "time"
-
- shellwords "github.com/mattn/go-shellwords"
- "github.com/urfave/cli"
-)
-
-// build info
-var version = "dev"
-
-var logger = NewLogger("gaper")
-
-// exit statuses
-var exitStatusSuccess = 0
-var exitStatusError = 1
-
-// Config contains all settings supported by gaper
-type Config struct {
- BinName string
- BuildPath string
- BuildArgs []string
- BuildArgsMerged string
- ProgramArgs []string
- Verbose bool
- WatchItems []string
- IgnoreItems []string
- PollInterval int
- Extensions []string
- NoRestartOn string
-}
-
-func main() {
- parseArgs := func(c *cli.Context) *Config {
- return &Config{
- BinName: c.String("bin-name"),
- BuildPath: c.String("build-path"),
- BuildArgsMerged: c.String("build-args"),
- ProgramArgs: c.Args(),
- Verbose: c.Bool("verbose"),
- WatchItems: c.StringSlice("watch"),
- IgnoreItems: c.StringSlice("ignore"),
- PollInterval: c.Int("poll-interval"),
- Extensions: c.StringSlice("extensions"),
- NoRestartOn: c.String("no-restart-on"),
- }
- }
-
- app := cli.NewApp()
- app.Name = "gaper"
- app.Usage = "Used to restart programs when they crash or a watched file changes"
- app.Version = version
-
- app.Action = func(c *cli.Context) {
- args := parseArgs(c)
- if err := runGaper(args); err != nil {
- logger.Error(err)
- os.Exit(1)
- }
- }
-
- // supported arguments
- app.Flags = []cli.Flag{
- cli.StringFlag{
- Name: "bin-name",
- Usage: "name for the binary built by Gaper for the executed program",
- },
- cli.StringFlag{
- Name: "build-path",
- Usage: "path to the program source code",
- },
- cli.StringFlag{
- Name: "build-args",
- Usage: "build arguments passed to the program",
- },
- cli.BoolFlag{
- Name: "verbose",
- Usage: "turns on the verbose messages from Gaper",
- },
- cli.StringSliceFlag{
- Name: "watch, w",
- Usage: "list of folders or files to watch for changes",
- },
- cli.StringSliceFlag{
- Name: "ignore, i",
- Usage: "list of folders or files to ignore for changes",
- },
- cli.IntFlag{
- Name: "poll-interval, p",
- Usage: "how often in milliseconds to poll watched files for changes",
- },
- cli.StringSliceFlag{
- Name: "extensions, e",
- Usage: "a comma-delimited list of file extensions to watch for changes",
- },
- cli.StringFlag{
- Name: "no-restart-on, n",
- Usage: "don't automatically restart the supervised program if it ends:\n" +
- "\t\tif \"error\", an exit code of 0 will still restart.\n" +
- "\t\tif \"exit\", no restart regardless of exit code.\n" +
- "\t\tif \"success\", no restart only if exit code is 0.",
- },
- }
-
- if err := app.Run(os.Args); err != nil {
- logger.Errorf("Error running gaper: %v", err)
- os.Exit(1)
- }
-}
-
-// nolint: gocyclo
-func runGaper(cfg *Config) error {
- var err error
- logger.Verbose(cfg.Verbose)
- logger.Debug("Starting gaper")
-
- if len(cfg.BuildArgs) == 0 && len(cfg.BuildArgsMerged) > 0 {
- cfg.BuildArgs, err = shellwords.Parse(cfg.BuildArgsMerged)
- if err != nil {
- return err
- }
- }
-
- wd, err := os.Getwd()
- if err != nil {
- return err
- }
-
- if len(cfg.WatchItems) == 0 {
- cfg.WatchItems = append(cfg.WatchItems, cfg.BuildPath)
- }
-
- logger.Debug("Settings: ")
- logger.Debug(" | bin: ", cfg.BinName)
- logger.Debug(" | build path: ", cfg.BuildPath)
- logger.Debug(" | build args: ", cfg.BuildArgs)
- logger.Debug(" | verbose: ", cfg.Verbose)
- logger.Debug(" | watch: ", cfg.WatchItems)
- logger.Debug(" | ignore: ", cfg.IgnoreItems)
- logger.Debug(" | poll interval: ", cfg.PollInterval)
- logger.Debug(" | extensions: ", cfg.Extensions)
- logger.Debug(" | no restart on: ", cfg.NoRestartOn)
- logger.Debug(" | working directory: ", wd)
-
- builder := NewBuilder(cfg.BuildPath, cfg.BinName, wd, cfg.BuildArgs)
- runner := NewRunner(os.Stdout, os.Stderr, filepath.Join(wd, builder.Binary()), cfg.ProgramArgs)
-
- if err = builder.Build(); err != nil {
- return fmt.Errorf("build error: %v", err)
- }
-
- shutdown(runner)
-
- if _, err = runner.Run(); err != nil {
- return fmt.Errorf("run error: %v", err)
- }
-
- watcher, err := NewWatcher(cfg.PollInterval, cfg.WatchItems, cfg.IgnoreItems, cfg.Extensions)
- if err != nil {
- return fmt.Errorf("watcher error: %v", err)
- }
-
- var changeRestart bool
-
- go watcher.Watch()
- for {
- select {
- case event := <-watcher.Events:
- logger.Debug("Detected new changed file: ", event)
- changeRestart = true
- if err := restart(builder, runner); err != nil {
- return err
- }
- case err := <-watcher.Errors:
- return fmt.Errorf("error on watching files: %v", err)
- case err := <-runner.Errors():
- if changeRestart {
- changeRestart = false
- } else {
- logger.Debug("Detected program exit: ", err)
- if err = handleProgramExit(builder, runner, err, cfg.NoRestartOn); err != nil {
- return err
- }
- }
- default:
- time.Sleep(time.Duration(cfg.PollInterval) * time.Millisecond)
- }
- }
-}
-
-func restart(builder Builder, runner Runner) error {
- logger.Debug("Restarting program")
-
- // kill process if it is running
- if !runner.Exited() {
- if err := runner.Kill(); err != nil {
- return fmt.Errorf("kill error: %v", err)
- }
- }
-
- if err := builder.Build(); err != nil {
- return fmt.Errorf("build error: %v", err)
- }
-
- if _, err := runner.Run(); err != nil {
- return fmt.Errorf("run error: %v", err)
- }
-
- return nil
-}
-
-func handleProgramExit(builder Builder, runner Runner, err error, noRestartOn string) error {
- exiterr, ok := err.(*exec.ExitError)
- if !ok {
- return fmt.Errorf("couldn't handle program crash restart: %v", err)
- }
-
- status, oks := exiterr.Sys().(syscall.WaitStatus)
- if !oks {
- return fmt.Errorf("couldn't resolve exit status: %v", err)
- }
-
- exitStatus := status.ExitStatus()
-
- // if "error", an exit code of 0 will still restart.
- if noRestartOn == "error" && exitStatus == exitStatusError {
- return nil
- }
-
- // if "success", no restart only if exit code is 0.
- if noRestartOn == "success" && exitStatus == exitStatusSuccess {
- return nil
- }
-
- // if "exit", no restart regardless of exit code.
- if noRestartOn == "exit" {
- return nil
- }
-
- return restart(builder, runner)
-}
-
-func shutdown(runner Runner) {
- c := make(chan os.Signal, 2)
- signal.Notify(c, os.Interrupt, syscall.SIGTERM)
- go func() {
- s := <-c
- logger.Debug("Got signal: ", s)
- err := runner.Kill()
- if err != nil {
- logger.Error("Error killing: ", err)
- }
- os.Exit(1)
- }()
-}
diff --git a/runner.go b/runner.go
index fe2f3c7..e5fc1e3 100644
--- a/runner.go
+++ b/runner.go
@@ -1,4 +1,4 @@
-package main
+package gaper
import (
"errors"
diff --git a/runner_test.go b/runner_test.go
index 2bca40c..05814d8 100644
--- a/runner_test.go
+++ b/runner_test.go
@@ -1,4 +1,4 @@
-package main
+package gaper
import (
"bytes"
@@ -28,6 +28,7 @@ func TestRunnerSuccessRun(t *testing.T) {
errCmd := <-runner.Errors()
assert.Nil(t, errCmd, "async error running binary")
assert.Contains(t, stdout.String(), "Gaper Test Message")
+ assert.Equal(t, stderr.String(), "")
}
func TestRunnerSuccessKill(t *testing.T) {
diff --git a/testdata/print-gaper.bat b/testdata/print-gaper.bat
index b067a80..b38561e 100644
--- a/testdata/print-gaper.bat
+++ b/testdata/print-gaper.bat
@@ -1,2 +1,2 @@
-timeout 2 > nul
+timeout 2 2>NUL
@echo Gaper Test Message
diff --git a/watcher.go b/watcher.go
index d125250..d3bde0e 100644
--- a/watcher.go
+++ b/watcher.go
@@ -1,4 +1,4 @@
-package main
+package gaper
import (
"errors"
@@ -10,12 +10,6 @@ import (
zglob "github.com/mattn/go-zglob"
)
-// DefaultExtensions used by the watcher
-var DefaultExtensions = []string{"go"}
-
-// DefaultPoolInterval used by the watcher
-var DefaultPoolInterval = 500
-
// Watcher is a interface for the watch process
type Watcher struct {
PollInterval int
@@ -123,7 +117,7 @@ func resolveGlobMatches(paths []string) ([]string, error) {
for _, path := range paths {
matches, err := zglob.Glob(path)
if err != nil {
- return nil, fmt.Errorf("couldn't resolve glob path %s: %v", path, err)
+ return nil, fmt.Errorf("couldn't resolve glob path \"%s\": %v", path, err)
}
logger.Debugf("Resolved glob path %s: %v", path, matches)
diff --git a/watcher_test.go b/watcher_test.go
index b0201bd..90c176a 100644
--- a/watcher_test.go
+++ b/watcher_test.go
@@ -1,4 +1,4 @@
-package main
+package gaper
import (
"os"