Compare commits

..

No commits in common. "master" and "v0.3.1" have entirely different histories.

8 changed files with 114 additions and 170 deletions

View file

@ -1,56 +0,0 @@
version: '3'
vars:
UNAME:
sh: uname
DOCKER_EXE: /usr/src/listen/out/{{OS}}/listen
tasks:
default:
deps:
- build:go
build:go:
aliases:
- build
cmds:
- "go build -o out/{{OS}}/ ."
preconditions:
- sh: "which go"
msg: Go is not installed. Install Go or build with Docker using the "docker" task
build:docker:
aliases:
- docker
cmds:
- task: build:docker:dockercli
- "docker build -t listen-build -f docker/build.Dockerfile --build-arg GOOS='{{OS}}' --build-arg GOARCH='{{ARCH}}' ."
- "mkdir -p out/{{OS}}"
- "docker create --name listen-build-tmp listen-build"
- cmd: "docker cp listen-build-tmp:{{.DOCKER_EXE}}.exe out/{{OS}}/"
platforms:
- windows
- cmd: "docker cp listen-build-tmp:{{.DOCKER_EXE}} out/{{OS}}/"
platforms:
- darwin
- linux
- "docker rm listen-build-tmp"
preconditions:
- sh: "uname | grep -qe Linux -e MINGW -e Darwin"
msg: "This task cannot be ran on OS: {{OS}}"
- sh: "which docker"
msg: Docker is not installed. Install Docker or build with Go using the "build" task
build:docker:dockercli:
cmds:
- cmd: '"C:\Program Files\Docker\Docker\DockerCli.exe" -SwitchLinuxEngine'
platforms:
- windows
clean:
cmds:
- rm -rf out/
- cmd: docker rm listen-build-tmp
ignore_error: true
- cmd: docker image rm listen-build
ignore_error: true

49
build Executable file
View file

@ -0,0 +1,49 @@
#!/bin/sh
# Create all the different builds for listen
# verify we are at root of repository
if ! [ -d .git ]; then
echo build: this script must be run at the root of the repo
exit 1
fi
if uname | grep -qe Linux; then
os=linux
elif uname | grep -qe MINGW; then
os=windows
else
os=other
fi
build_go() {
go build -o out/$(go env GOOS)/listen .
}
build_docker() {
if [ $os = "windows" ]; then
/c/Program\ Files/Docker/Docker/DockerCli.exe -SwitchWindowsEngine
exe=C:/build/listen/listen.exe
else
exe=/usr/src/listen/listen
fi
if docker build -t listen-build -f docker/build.$os.Dockerfile .; then
mkdir -p out/$os
docker create --name listen-build-tmp listen-build
docker cp listen-build-tmp:$exe out/$os
docker rm listen-build-tmp
fi
}
# if an arg is specified, force building with the specified method
[ $1 ] && (build_$1; exit)
# prefer building with local go install if it exists on path
if which go &> /dev/null; then
build_go; exit
# if windows or linux, try building with docker
elif echo $os | grep -qe linux -e windows; then
which docker &> /dev/null && (build_docker; exit)
fi
echo could not find valid build method for OS && exit 2

View file

@ -1,16 +0,0 @@
# This Dockerfile is meant for building listen ONLY
# listen is currently not intended to run in a Docker container
FROM golang:1-alpine AS build
WORKDIR /usr/src/listen
COPY . .
RUN apk upgrade --no-cache
ARG GOOS="linux"
RUN go env -w GOOS=${GOOS}
ARG GOARCH="amd64"
RUN go env -w GOARCH=${GOARCH}
RUN go build -o out/$(go env GOOS)/ .
CMD ["tail", "-f", "/dev/null"]

View file

@ -0,0 +1,14 @@
# This Dockerfile is meant for building listen for Linux ONLY
# listen is currently not intended to run in a Docker container
ARG IMAGE="1-alpine"
FROM golang:${IMAGE} AS build
WORKDIR /usr/src/listen
COPY . .
RUN apk upgrade --no-cache
RUN go build .
CMD ["tail", "-f", "/dev/null"]

View file

@ -0,0 +1,13 @@
# This Dockerfile is meant for building listen for Windows ONLY
# listen is currently not intended to run in a Docker container
ARG IMAGE="1-nanoserver"
FROM golang:${IMAGE} AS build
WORKDIR C:/build/listen
COPY . .
RUN go build .
CMD ["ping.exe", "-t", "localhost"]

View file

@ -17,14 +17,12 @@ import (
"github.com/fsnotify/fsnotify"
)
var triggered bool = false
type Listen struct {
FileMap map[string]bool
CksumMap map[string][]byte
IntervalMap map[string]time.Time
Condition string
NoCksum bool
Cksum bool
Interval time.Duration
Command []string
Quiet bool
@ -76,7 +74,6 @@ func allCheck(l Listen) bool {
}
func runTrigger(l Listen, f string, r bool, quit chan bool) bool {
triggered = true
if !l.Quiet && !r {
if l.Condition == "any" {
printStatus(fmt.Sprintf("& File %s modified. ", f))
@ -97,32 +94,12 @@ func runTrigger(l Listen, f string, r bool, quit chan bool) bool {
log.Fatalln(err)
}
exit := make(chan bool)
s := make(chan os.Signal, 1)
signal.Notify(s, os.Interrupt)
go func() {
if err := cmd.Wait(); err != nil {
if rc, ok := err.(*exec.ExitError); ok {
if !l.Quiet {
switch rc.ExitCode() {
case -1:
color.New(color.FgYellow).Fprintf(os.Stderr, "\n& WARNING: Command interrupted with ^C.\n")
default:
color.New(color.FgYellow).Fprintf(os.Stderr, "& WARNING: Command exited with code %d.\n", rc.ExitCode())
}
}
if err := cmd.Wait(); err != nil {
if rc, ok := err.(*exec.ExitError); ok {
if !l.Quiet {
color.New(color.FgYellow).Fprintf(os.Stderr, "& WARNING: Command exited with code %d.\n", rc.ExitCode())
}
}
exit <- true
}()
select {
case <-s:
cmd.Process.Signal(os.Interrupt)
<-exit
case <-exit:
}
if !l.Quiet {
@ -134,11 +111,9 @@ func runTrigger(l Listen, f string, r bool, quit chan bool) bool {
}
quit <- true
triggered = false
return true
}
triggered = false
return false
}
@ -165,7 +140,13 @@ func loopFsnotify(l Listen, quit chan bool) {
for key := range l.FileMap {
if event.Name == key {
if l.NoCksum {
if l.Cksum {
// return value indicates a break is needed
if cksumCheck(l, key, &trigger) {
fileMod = key
break
}
} else {
if event.Has(fsnotify.Write) {
if l.Condition == "any" {
fileMod = key
@ -188,12 +169,6 @@ func loopFsnotify(l Listen, quit chan bool) {
l.FileMap[key] = true
}
} else {
// return value indicates a break is needed
if cksumCheck(l, key, &trigger) {
fileMod = key
break
}
}
}
} // end for
@ -231,7 +206,13 @@ func loopInterval(l Listen, quit chan bool) {
trigger := false
for key := range l.FileMap {
if l.NoCksum {
if l.Cksum {
// return value indicates a break is needed
if cksumCheck(l, key, &trigger) {
fileMod = key
break
}
} else {
s, err := os.Stat(key)
if err != nil {
log.Fatal()
@ -247,12 +228,7 @@ func loopInterval(l Listen, quit chan bool) {
l.FileMap[key] = true
}
} else {
// return value indicates a break is needed
if cksumCheck(l, key, &trigger) {
fileMod = key
break
}
}
} // end for key
@ -286,7 +262,7 @@ func startMessage(l Listen) {
printStatus("& listen will exit when ")
}
if !l.NoCksum {
if l.Cksum {
printStatus("the checksum of ")
}
@ -302,10 +278,10 @@ func startMessage(l Listen) {
printStatus("this file has ")
}
if l.NoCksum {
printStatus("been modified:\n")
} else {
if l.Cksum {
printStatus("changed:\n")
} else {
printStatus("been modified:\n")
}
for key := range l.FileMap {
@ -313,9 +289,8 @@ func startMessage(l Listen) {
}
}
func (l Listen) Run() int {
func (l Listen) Run() {
// catch ^C
ret := 0
quit := make(chan bool)
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
@ -342,14 +317,7 @@ func (l Listen) Run() int {
select {
case <-c:
for {
if !triggered {
fmt.Println()
ret = 130
break
}
<-c
}
fmt.Println()
case <-quit:
}
@ -357,5 +325,4 @@ func (l Listen) Run() int {
printStatus("& Exiting...\n")
}
return ret
}

42
main.go
View file

@ -13,16 +13,16 @@ import (
flags "github.com/jessevdk/go-flags"
)
const VERSION string = "v0.4.1"
const VERSION string = "v0.3.1"
type Options struct {
Version bool `short:"v" long:"version" description:"Displays version info and exits"`
Quiet bool `short:"q" long:"quiet" description:"Suppresses status messages (stderr lines beginning with '&')"`
Files []string `short:"f" long:"file" description:"File(s) to listen to (watch)" value-name:"FILE"`
Condition string `short:"w" long:"when" description:"If multiple files are specified, choose if any file or all files specified are needed to trigger COMMAND" default:"any" choice:"any" choice:"all"`
NoChecksum bool `short:"x" long:"no-checksum" description:"Do not calculate checksum as an additional check for file changes (see MODES section in man page for more info)"`
Interval string `short:"i" long:"interval" description:"Use this time interval (ex. 5m30s, 1s) between filesystem checks instead of watching kernel events. If the interval is effectively 0 (the default), kernel events are used" default:"0s"`
RunFirst bool `short:"r" long:"run" description:"Runs COMMAND (if specified) before starting listen"`
Version bool `short:"v" long:"version" description:"Displays version info and exits"`
Quiet bool `short:"q" long:"quiet" description:"Suppresses status messages (stderr lines beginning with '&')"`
Files []string `short:"f" long:"file" description:"File(s) to listen to (watch)" value-name:"FILE"`
Condition string `short:"w" long:"when" description:"If multiple files are specified, choose if any file or all files specified are needed to trigger COMMAND" default:"any" choice:"any" choice:"all"`
Checksum bool `short:"c" long:"checksum" description:"Use checksum to determine when file(s) are changed instead of writes/modification time"`
Interval string `short:"i" long:"interval" description:"Use this time interval (ex. 5m30s, 1s) between filesystem checks instead of watching kernel events. If the interval is effectively 0 (the default), kernel events are used" default:"0s"`
RunFirst bool `short:"r" long:"run" description:"Runs COMMAND (if specified) before starting listen"`
}
func validateArgs(opts Options, commandLen int) error {
@ -66,7 +66,7 @@ func validateArgs(opts Options, commandLen int) error {
return nil
}
func setup(opts Options, command []string) int {
func setup(opts Options, command []string) {
var filesMap map[string]bool = make(map[string]bool)
var cksumMap map[string][]byte = make(map[string][]byte)
var intervalMap map[string]time.Time = make(map[string]time.Time)
@ -103,40 +103,22 @@ func setup(opts Options, command []string) int {
}
if success {
l := Listen{filesMap, cksumMap, intervalMap, opts.Condition, opts.NoChecksum, intervalDuration, command, opts.Quiet, opts.RunFirst}
return l.Run()
l := Listen{filesMap, cksumMap, intervalMap, opts.Condition, opts.Checksum, intervalDuration, command, opts.Quiet, opts.RunFirst}
l.Run()
}
return -1
}
func main() {
ret := 0
opts := Options{}
parser := flags.NewParser(&opts, flags.Default)
parser.Usage = "[OPTIONS] -- [COMMAND]"
remaining, err := parser.Parse()
defer func() {
if err := recover(); err != nil {
switch ret {
case -1:
os.Exit(5)
default:
os.Exit(ret)
}
}
}()
if err == nil {
if opts.Version {
fmt.Printf("listen %s\n", VERSION)
} else if err := validateArgs(opts, len(remaining)); err == nil {
ret = setup(opts, remaining)
if ret != 0 {
panic(ret)
}
setup(opts, remaining)
}
}
}

View file

@ -1,9 +0,0 @@
% listen | General Commands Manual
# NAME
listen - A simple watcher program that runs commands when specified files are modified
# SYNOPSIS
listen [OPTIONS] -- [COMMAND]