From 9a53ac524bac3eb8067266d894b545a4b2ad535b Mon Sep 17 00:00:00 2001 From: kc merrill Date: Fri, 3 Mar 2017 22:15:47 -0700 Subject: [PATCH 1/2] A lot of enhancements/house cleaning - Added () to task definitions - Moving task module to alfred(rest coming soon) - Formatting - Beautifying the readme --- README.md | 40 +++-- GUIDE.md => RTFM.md | 0 alfred.yml | 1 + alfred/alfred.go | 33 ++-- {task => alfred}/task.go | 146 ++++++++++-------- alfred_test.go | 3 +- .../part-one.alfred.yml} | 0 .../.alfred/part-two.alfred.yml | 7 + modules/self/alfred.yml | 4 +- 9 files changed, 140 insertions(+), 94 deletions(-) rename GUIDE.md => RTFM.md (100%) rename {task => alfred}/task.go (66%) rename examples/demo-everything/{alfred.yml => .alfred/part-one.alfred.yml} (100%) create mode 100644 examples/demo-everything/.alfred/part-two.alfred.yml diff --git a/README.md b/README.md index b30499b..ebd4cd4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # alfred + Because even Batman needs a little help. [![Build Status](https://travis-ci.org/kcmerrill/alfred.svg?branch=master)](https://travis-ci.org/kcmerrill/alfred) [![Join the chat at https://gitter.im/kcmerrill/alfred](https://badges.gitter.im/kcmerrill/alfred.svg)](https://gitter.im/kcmerrill/alfred?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) @@ -6,9 +7,11 @@ Because even Batman needs a little help. ![Alfred](https://raw.githubusercontent.com/kcmerrill/alfred/master/assets/alfred.jpg "Alfred") ## What is it + A simple go/yaml powered make file/task runner with a bit of a twist. ## Binaries || Installation + [![MacOSX](https://raw.githubusercontent.com/kcmerrill/go-dist/master/assets/apple_logo.png "Mac OSX")] (http://go-dist.kcmerrill.com/kcmerrill/alfred/mac/amd64) [![Linux](https://raw.githubusercontent.com/kcmerrill/go-dist/master/assets/linux_logo.png "Linux")] (http://go-dist.kcmerrill.com/kcmerrill/alfred/linux/amd64) via go: @@ -20,19 +23,22 @@ via docker: `$ docker run -v $PWD:$PWD -w $PWD kcmerrill/alfred` ## Features + - Extendable. Common tasks(Private too) - Watch files for modifications -- Retry/Rerun tasks based on failures before giving up +- Retry/Rerun tasks based on failures before giving up - Logging - Success/Failure decision tree -- Run tasks asynchronously or synchronously +- Run tasks asynchronously or synchronously - Autocomplete task names - Static webserver -- Many more! +- Many more! ## Usage + Create a file named: `alfred.yml` -``` + +```yaml say.hello: summary: I will say hello! usage: alfred say.hello @@ -65,24 +71,30 @@ Then, anywhere in the top-level or child directories to the `alfred.yml` file: `alfred blurt` will perform both tasks at the same time -# Quick Walkthrough -[![asciicast](https://asciinema.org/a/103711.png)](https://asciinema.org/a/103711) +## Quick Walkthrough + +To see a comprehensive list of features and functionality please [RTFM](RTFM.md "additional documentation") -[For additional documentation, please refer to the GUIDE](GUIDE.md "additional documentation") +[![asciicast](https://asciinema.org/a/103711.png)](https://asciinema.org/a/103711) ## Example uses -* Monitor websites -* Setup/Update/Deploy projects in your dev env -* Simple Nagios, Jenkins, pingdom replacement -* Monitor crons(alert on failures, update endpoints etc ... ) -* Watch for file modifications to run tests->builds +- Monitor websites +- Setup/Update/Deploy projects in your dev env +- Simple Nagios, Jenkins, pingdom replacement +- Monitor crons(alert on failures, update endpoints etc ... ) +- Watch for file modifications to run tests->builds ## Tab completion -Copy the included `alfred.completion.sh` to `/etc/bash_completion.d/`, or source it in your `~/.profile` file. +Copy the included `alfred.completion.sh` to `/etc/bash_completion.d/`. + +Or source it in your `~/.profile` file. + +Or `alfred /self tab.completion` ## Testing + You might say I've cheated the testing route by only scraping the output. You'd be right. -"I live with a wizard. I cheat" ~ Mouse +"I live with a wizard. I cheat" ~ Mouse \ No newline at end of file diff --git a/GUIDE.md b/RTFM.md similarity index 100% rename from GUIDE.md rename to RTFM.md diff --git a/alfred.yml b/alfred.yml index 65986f7..a284200 100644 --- a/alfred.yml +++ b/alfred.yml @@ -26,6 +26,7 @@ tdd: test: summary: Testing ... command: | + go install go test -v commit: diff --git a/alfred/alfred.go b/alfred/alfred.go index a970d63..4bd44c9 100644 --- a/alfred/alfred.go +++ b/alfred/alfred.go @@ -17,7 +17,6 @@ import ( "github.com/fatih/color" "github.com/kcmerrill/alfred/remote" - "github.com/kcmerrill/alfred/task" "gopkg.in/yaml.v2" ) @@ -32,7 +31,7 @@ type Alfred struct { // Variables Vars map[string]string `yaml:"alfred.vars"` // All of the tasks parsed from the yaml file - Tasks map[string]*task.Task `yaml:",inline"` + Tasks map[string]*Task `yaml:",inline"` // Alfred remotes(private/public repos) remote *remote.Remote // Originating directory @@ -56,6 +55,8 @@ func New(args []string) { // Grab the current directory and save if off a.dir, _ = os.Getwd() + fmt.Println(args) + // Set our Arguments a.args = args @@ -69,7 +70,7 @@ func New(args []string) { if !a.findTask() { a.args = append(a.args[:1], append([]string{"default"}, a.args[1:]...)...) if !a.findTask() { - say("ERROR", "Invalid task.") + say("ERROR", "Invalid taskkkkkkk.") os.Exit(1) } } @@ -117,7 +118,7 @@ func (a *Alfred) findTask() bool { return false } } else { - say(a.args[2], "invalid task.") + say(a.args[2], "Invalid taskk.") return false } break @@ -129,15 +130,15 @@ func (a *Alfred) findTask() bool { func (a *Alfred) runTask(task string, args []string, formatted bool) bool { // Verify again it's a valid task if !a.isValidTask(task) { - say(task, "Invalid task.") + say(task, "Invalid task."+task+"--") return false } // Infinite loop Used for the every command for { // Run our setup tasks - for _, s := range a.Tasks[task].SetupTasks() { - if !a.runTask(s, args, formatted) { + for _, taskDefinition := range a.Tasks[task].TaskGroup(a.Tasks[task].Setup, args) { + if !a.runTask(taskDefinition.Name, taskDefinition.Params, formatted) { break } } @@ -177,7 +178,7 @@ func (a *Alfred) runTask(task string, args []string, formatted bool) bool { m, _ := regexp.Match(a.Tasks[task].Watch, []byte(path)) if m { // If not a match ... - return errors.New("") + return errors.New("no matches") } } return nil @@ -248,8 +249,8 @@ func (a *Alfred) runTask(task string, args []string, formatted bool) bool { fmt.Println(red("✘"), task) // Failed? Lets run the failed tasks - for _, failed := range a.Tasks[task].FailedTasks() { - if !a.runTask(failed, args, formatted) { + for _, taskDefinition := range a.Tasks[task].TaskGroup(a.Tasks[task].Fail, args) { + if !a.runTask(taskDefinition.Name, taskDefinition.Params, formatted) { break } } @@ -274,26 +275,26 @@ func (a *Alfred) runTask(task string, args []string, formatted bool) bool { var wg sync.WaitGroup // Do we have any tasks we need to run in parallel? - for _, t := range a.Tasks[task].MultiTask() { + for _, taskDefinition := range a.Tasks[task].TaskGroup(a.Tasks[task].Multitask, args) { wg.Add(1) go func(t string, args []string) { defer wg.Done() a.runTask(t, args, true) - }(t, args) + }(taskDefinition.Name, taskDefinition.Params) } wg.Wait() // Ok, we made it here ... Is this task a task group? - for _, t := range a.Tasks[task].TaskGroup() { - if !a.runTask(t, args, formatted) { + for _, taskDefinition := range a.Tasks[task].TaskGroup(a.Tasks[task].Tasks, args) { + if !a.runTask(taskDefinition.Name, taskDefinition.Params, formatted) { break } } // Woot! Lets run the ok tasks if taskok { - for _, okTasks := range a.Tasks[task].OkTasks() { - if !a.runTask(okTasks, args, formatted) { + for _, taskDefinition := range a.Tasks[task].TaskGroup(a.Tasks[task].Ok, args) { + if !a.runTask(taskDefinition.Name, taskDefinition.Params, formatted) { break } } diff --git a/task/task.go b/alfred/task.go similarity index 66% rename from task/task.go rename to alfred/task.go index b6af390..8d6e435 100644 --- a/task/task.go +++ b/alfred/task.go @@ -1,4 +1,4 @@ -package task +package alfred import ( "bufio" @@ -44,6 +44,8 @@ A brief explination: - Setup: Similiar to tasks but these get run _before_ the command/task group gets called - Serve: A string, denoting port number to serve a static webserver */ + +// Task contains a task definition and all of it's components type Task struct { Summary string Test string @@ -76,6 +78,12 @@ type Task struct { Serve string } +// TaskDefinition defines the name and params of a task when originally in string format +type TaskDefinition struct { + Name string + Params []string +} + // IsPrivate returns true/false if the task is private(not executable individually) func (t *Task) IsPrivate() bool { return t.Private @@ -96,24 +104,49 @@ func (t *Task) Aliases() []string { return strings.Fields(t.Alias) } -func (t *Task) FailedTasks() []string { - return strings.Fields(t.Fail) -} - -func (t *Task) OkTasks() []string { - return strings.Fields(t.Ok) -} - -func (t *Task) TaskGroup() []string { - return strings.Fields(t.Tasks) -} - -func (t *Task) MultiTask() []string { - return strings.Fields(t.Multitask) -} +//TaskGroup takes in a string(bleh(1234) whatever(bleh, woot)) and returns the values and args +func (t *Task) TaskGroup(tasks string, args []string) []TaskDefinition { + results := make([]TaskDefinition, 0) + if tasks == "" { + // If there is nothing, then there is nothing to report + return results + } + if strings.Index(tasks, "(") == -1 { + // This means we have a regular space delimited list + tasks := strings.Split(tasks, " ") + for _, task := range tasks { + results = append(results, TaskDefinition{Name: task, Params: args}) + } + } else { + // This means we have a group of tasks() + // Not going to do a ton of error checking. + // Don't be a sad panda and forget to add () to _EVERY_ task! + definitions := strings.Split(tasks, ")") + for _, task := range definitions { + // Clean up the task in case + task = strings.TrimSpace(task) + if task == "" { + // Empty task? Continue ... + continue + } + // Now, lets separate the task from the params + if len(strings.Split(task, "(")) == 1 { + // No args + results = append(results, TaskDefinition{Name: strings.TrimSpace(strings.Split(task, "(")[0]), Params: args}) + } else { + taskName := strings.TrimSpace(strings.Split(task, "(")[0]) + p := strings.TrimSpace(strings.Split(task, "(")[1]) + params := strings.Split(p, ",") + for idx, param := range params { + // lets clean up our params + params[idx] = strings.TrimSpace(param) + } + results = append(results, TaskDefinition{Name: taskName, Params: params}) + } + } + } -func (t *Task) SetupTasks() []string { - return strings.Fields(t.Setup) + return results } // RunCommand runs a command, also determining if it needs to be formated(multitasks for example) @@ -134,18 +167,18 @@ func (t *Task) RunCommand(cmd, name string, formatted bool) bool { return true } -//CommandComplex takes in a command and will write it to a file, or special formatting(multitask for example) +// CommandComplex takes in a command and will write it to a file, or special formatting(multitask for example) func (t *Task) CommandComplex(cmd, name string) bool { if cmd != "" { var l *os.File var err error - /* If log is set ... lets use it */ + // If log is set ... lets use it if t.Log != "" { l, err = os.OpenFile(t.Log, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755) if err != nil { - /* Don't quit ... but don't log either */ + // Don't quit ... but don't log either t.Log = "" } defer l.Close() @@ -198,11 +231,11 @@ func (t *Task) CommandComplex(cmd, name string) bool { return false } } - /* If there was no command to run, then don't fail the task */ + // If there was no command to run, then don't fail the task return true } -/* Execute a task ... */ +// CommandBasic runs a basic command, no frills func (t *Task) CommandBasic(cmd string) bool { if cmd != "" { cmd := exec.Command("bash", "-c", cmd) @@ -210,39 +243,30 @@ func (t *Task) CommandBasic(cmd string) bool { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if cmd.Run() == nil { - /* Was it succesful? */ + // Was it succesful? return true - } else { - return false } + return false } - /* If there was no command to run, then don't fail the task */ + // If there was no command to run, then don't fail the task return true } -/* Test - Currently lets just run a command, BUT, in the future I'd love to see: - file.exists "filename" - dir.exists "dirname" - dir.age 15m - file.age 15m - etc etc .. -*/ +// TestF runs a command with _NO_ output! func (t *Task) TestF(tst string) bool { if tst != "" { cmd := exec.Command("bash", "-c", tst) if cmd.Run() == nil { /* Was it succesful? */ return true - } else { - return false } + return false } /* If there was no command to run, then don't fail the test */ return true } -/* Evaluate */ +// Eval runs a string to see if it's a command or not(depending on it's exit code) func (t *Task) Eval(cmd string) string { out, err := exec.Command(cmd).Output() if err != nil { @@ -259,12 +283,12 @@ func (t *Task) Prepare(args []string, vars map[string]string) bool { t.Vars = make(map[string]string, 0) } - /* override variable defaults with actual vars */ + // override variable defaults with actual vars for key, value := range vars { t.Vars[key] = t.Eval(value) } - /* override defaults with the args */ + // override defaults with the args for index, value := range args { if len(t.Args) > index { t.Args[index] = value @@ -273,7 +297,7 @@ func (t *Task) Prepare(args []string, vars map[string]string) bool { } } - /* Any null values? If so, bail ... */ + // Any null values? If so, bail ... for _, value := range t.Args { if value == "" { return false @@ -292,65 +316,63 @@ func (t *Task) Prepare(args []string, vars map[string]string) bool { // Setup time t.Time = time.Now() - /* All of the modules */ + // All of the modules for key, value := range t.Modules { - if module_ok, module_translated := t.template(value); module_ok { - t.Modules[key] = module_translated + if moduleOk, moduleTranslated := t.template(value); moduleOk { + t.Modules[key] = moduleTranslated } else { return false } } - /* get to translating */ - if every_ok, every_translated := t.template(t.Every); every_ok { - t.Every = every_translated + // get to translating + if everyOk, everyTranslated := t.template(t.Every); everyOk { + t.Every = everyTranslated } else { return false } - if allargs_ok, allargs_translated := t.template(strings.Join(args, " ")); allargs_ok { - t.AllArgs = allargs_translated + if allargsOk, allargsTranslated := t.template(strings.Join(args, " ")); allargsOk { + t.AllArgs = allargsTranslated } else { return false } - if cmd_ok, cmd_translated := t.template(t.Command); cmd_ok { - t.Command = cmd_translated + if cmdOk, cmdTranslated := t.template(t.Command); cmdOk { + t.Command = cmdTranslated } else { return false } - if cmd_ok, cmd_translated := t.template(t.Commands); cmd_ok { - t.Commands = cmd_translated + if cmdOk, cmdTranslated := t.template(t.Commands); cmdOk { + t.Commands = cmdTranslated } else { return false } - if dir_ok, dir_translated := t.template(t.Dir); dir_ok { - t.Dir = dir_translated + if dirOk, dirTranslated := t.template(t.Dir); dirOk { + t.Dir = dirTranslated } else { return false } - if tst_ok, tst_translated := t.template(t.Test); tst_ok { - t.Test = tst_translated + if tstOk, tstTranslated := t.template(t.Test); tstOk { + t.Test = tstTranslated } else { return false } - /* if we made it here, then we are good to go */ + // if we made it here, then we are good to go return true } -/* Translate a string to a template */ +// template a helper function to translate a string to a template func (t *Task) template(translate string) (bool, string) { template := template.Must(template.New("translate").Parse(translate)) b := new(bytes.Buffer) err := template.Execute(b, t) if err == nil { return true, b.String() - } else { - return false, translate } - return true, translate + return false, translate } diff --git a/alfred_test.go b/alfred_test.go index df2d539..1b51751 100644 --- a/alfred_test.go +++ b/alfred_test.go @@ -32,6 +32,7 @@ func run(cmd string, t *testing.T) (string, error) { } else { return strings.Trim(string(out), "\n"), err } + } func TestCurrentDirectory(t *testing.T) { @@ -254,7 +255,7 @@ func TestArgumentsOkAndDefaults(t *testing.T) { t.FailNow() } /* Verfify our command had run succesfully */ - if !strings.Contains(sut, "alfred.yml") { + if !strings.Contains(sut, ".alfred") { t.Logf("Listing of the current directory should show an alfred.yml file") t.FailNow() } diff --git a/examples/demo-everything/alfred.yml b/examples/demo-everything/.alfred/part-one.alfred.yml similarity index 100% rename from examples/demo-everything/alfred.yml rename to examples/demo-everything/.alfred/part-one.alfred.yml diff --git a/examples/demo-everything/.alfred/part-two.alfred.yml b/examples/demo-everything/.alfred/part-two.alfred.yml new file mode 100644 index 0000000..c086dcd --- /dev/null +++ b/examples/demo-everything/.alfred/part-two.alfred.yml @@ -0,0 +1,7 @@ +thirty.eight: + tasks: fourty(kc, merrill) fourty(clark, kent ) fourty( bruce , wayne ) + +fourty: + summary: Takes two args, tests the () functionality + command: | + echo -{{ index .Args 0 }}-{{ index .Args 1 }}- diff --git a/modules/self/alfred.yml b/modules/self/alfred.yml index aebbf1f..fae484e 100644 --- a/modules/self/alfred.yml +++ b/modules/self/alfred.yml @@ -42,7 +42,9 @@ tab.completion: summary: Install tab completion test: which wget commands: | - mkdir -p /etc/bash_completion.d/ + echo "Need to request sudo access to run:" + echo "mkdir -p /etc/bash_completion.d" + sudo mkdir -p /etc/bash_completion.d/ cd /etc/bash_completion.d/ rm -rf alfred.completion.sh wget https://raw.githubusercontent.com/kcmerrill/alfred/master/alfred.completion.sh From 01a816559bc6ef80ba533f5e674f96aa088896d6 Mon Sep 17 00:00:00 2001 From: kc merrill Date: Fri, 3 Mar 2017 23:02:50 -0700 Subject: [PATCH 2/2] Added tests for task/parms --- alfred/alfred.go | 81 +++++++++++++++++++++++++----------------------- alfred_test.go | 38 ++++++++++++++++------- 2 files changed, 68 insertions(+), 51 deletions(-) diff --git a/alfred/alfred.go b/alfred/alfred.go index 4bd44c9..943f5d1 100644 --- a/alfred/alfred.go +++ b/alfred/alfred.go @@ -31,7 +31,7 @@ type Alfred struct { // Variables Vars map[string]string `yaml:"alfred.vars"` // All of the tasks parsed from the yaml file - Tasks map[string]*Task `yaml:",inline"` + Tasks map[string]Task `yaml:",inline"` // Alfred remotes(private/public repos) remote *remote.Remote // Originating directory @@ -55,8 +55,6 @@ func New(args []string) { // Grab the current directory and save if off a.dir, _ = os.Getwd() - fmt.Println(args) - // Set our Arguments a.args = args @@ -70,7 +68,7 @@ func New(args []string) { if !a.findTask() { a.args = append(a.args[:1], append([]string{"default"}, a.args[1:]...)...) if !a.findTask() { - say("ERROR", "Invalid taskkkkkkk.") + say("ERROR", "Invalid task.") os.Exit(1) } } @@ -103,7 +101,8 @@ func (a *Alfred) findTask() bool { break // Called a local task case len(a.args) >= 2 && !a.isRemote(): - if a.isValidTask(a.args[1]) && !a.Tasks[a.args[1]].IsPrivate() { + task := a.Tasks[a.args[1]] + if a.isValidTask(a.args[1]) && !task.IsPrivate() { if !a.runTask(a.args[1], a.args[2:], false) { return false } @@ -113,12 +112,13 @@ func (a *Alfred) findTask() bool { break // Called a remote task case len(a.args) >= 3 && a.isRemote(): - if a.isValidTask(a.args[2]) && !a.Tasks[a.args[2]].IsPrivate() { + task := a.Tasks[a.args[2]] + if a.isValidTask(a.args[2]) && !task.IsPrivate() { if !a.runTask(a.args[2], a.args[3:], false) { return false } } else { - say(a.args[2], "Invalid taskk.") + say(a.args[2], "Invalid task") return false } break @@ -130,14 +130,16 @@ func (a *Alfred) findTask() bool { func (a *Alfred) runTask(task string, args []string, formatted bool) bool { // Verify again it's a valid task if !a.isValidTask(task) { - say(task, "Invalid task."+task+"--") + say(task, "Invalid task.") return false } + copyOfTask := a.Tasks[task] + // Infinite loop Used for the every command for { // Run our setup tasks - for _, taskDefinition := range a.Tasks[task].TaskGroup(a.Tasks[task].Setup, args) { + for _, taskDefinition := range copyOfTask.TaskGroup(copyOfTask.Setup, args) { if !a.runTask(taskDefinition.Name, taskDefinition.Params, formatted) { break } @@ -152,30 +154,31 @@ func (a *Alfred) runTask(task string, args []string, formatted bool) bool { } // Lets prep it, and if it's bunk, lets see if we can pump out it's usage - if !a.Tasks[task].Prepare(args, a.Vars) { + if !copyOfTask.Prepare(args, a.Vars) { say(task+":error", "Missing argument(s).") - return false + // No need in going on, programmer error + os.Exit(1) } // Lets change the directory if set - if a.Tasks[task].Dir != "" { - if err := os.Chdir(a.Tasks[task].Dir); err != nil { - if err := os.MkdirAll(a.Tasks[task].Dir, 0755); err != nil { + if copyOfTask.Dir != "" { + if err := os.Chdir(copyOfTask.Dir); err != nil { + if err := os.MkdirAll(copyOfTask.Dir, 0755); err != nil { say(task+":dir", "Invalid directory") return false } - os.Chdir(a.Tasks[task].Dir) + os.Chdir(copyOfTask.Dir) } } // We watching for files? - if a.Tasks[task].Watch != "" { + if copyOfTask.Watch != "" { // Regardless of what's going on, lets set every to 1s - a.Tasks[task].Every = "1s" + copyOfTask.Every = "1s" for { matched := filepath.Walk(".", func(path string, f os.FileInfo, err error) error { if f.ModTime().After(time.Now().Add(-2 * time.Second)) { - m, _ := regexp.Match(a.Tasks[task].Watch, []byte(path)) + m, _ := regexp.Match(copyOfTask.Watch, []byte(path)) if m { // If not a match ... return errors.New("no matches") @@ -193,8 +196,8 @@ func (a *Alfred) runTask(task string, args []string, formatted bool) bool { // Go through each of the modules ... // before command, docker stop for example - for module, cmd := range a.Tasks[task].Modules { - if !a.Tasks[task].RunCommand(args[0]+" "+a.remote.ModulePath(module)+" "+cmd, task, formatted) { + for module, cmd := range copyOfTask.Modules { + if !copyOfTask.RunCommand(args[0]+" "+a.remote.ModulePath(module)+" "+cmd, task, formatted) { // It failed :( taskok = false break @@ -202,16 +205,16 @@ func (a *Alfred) runTask(task string, args []string, formatted bool) bool { } // First, lets show the summary - if a.Tasks[task].Summary != "" { + if copyOfTask.Summary != "" { fmt.Println("") - say(task, a.Tasks[task].Summary) + say(task, copyOfTask.Summary) } // Test ... - if a.Tasks[task].TestF(a.Tasks[task].Test) { + if copyOfTask.TestF(copyOfTask.Test) { // Lets execute the command if it has one, and add retry logic - for x := 0; x < a.Tasks[task].Retry || x == 0; x++ { - taskok = a.Tasks[task].RunCommand(a.Tasks[task].Command, task, formatted) + for x := 0; x < copyOfTask.Retry || x == 0; x++ { + taskok = copyOfTask.RunCommand(copyOfTask.Command, task, formatted) if taskok { break } @@ -223,9 +226,9 @@ func (a *Alfred) runTask(task string, args []string, formatted bool) bool { // Commands, not to be misaken for command if taskok { - cmds := strings.Split(a.Tasks[task].Commands, "\n") + cmds := strings.Split(copyOfTask.Commands, "\n") for _, c := range cmds { - taskok = a.Tasks[task].RunCommand(c, task, formatted) + taskok = copyOfTask.RunCommand(c, task, formatted) if !taskok { break } @@ -233,12 +236,12 @@ func (a *Alfred) runTask(task string, args []string, formatted bool) bool { } // Handle Serve ... - if taskok && a.Tasks[task].Serve != "" { - go Serve(".", a.Tasks[task].Serve) + if taskok && copyOfTask.Serve != "" { + go Serve(".", copyOfTask.Serve) } // Wait ... - if waitDuration, waitError := time.ParseDuration(a.Tasks[task].Wait); waitError == nil { + if waitDuration, waitError := time.ParseDuration(copyOfTask.Wait); waitError == nil { <-time.After(waitDuration) } @@ -249,7 +252,7 @@ func (a *Alfred) runTask(task string, args []string, formatted bool) bool { fmt.Println(red("✘"), task) // Failed? Lets run the failed tasks - for _, taskDefinition := range a.Tasks[task].TaskGroup(a.Tasks[task].Fail, args) { + for _, taskDefinition := range copyOfTask.TaskGroup(copyOfTask.Fail, args) { if !a.runTask(taskDefinition.Name, taskDefinition.Params, formatted) { break } @@ -261,13 +264,13 @@ func (a *Alfred) runTask(task string, args []string, formatted bool) bool { } // Handle skips ... - if !taskok && a.Tasks[task].Skip { + if !taskok && copyOfTask.Skip { return false } // Handle exits ... - if !taskok && a.Tasks[task].Exit != "" { - if exitCode, err := strconv.Atoi(a.Tasks[task].Exit); err == nil { + if !taskok && copyOfTask.Exit != "" { + if exitCode, err := strconv.Atoi(copyOfTask.Exit); err == nil { os.Exit(exitCode) } return false @@ -275,7 +278,7 @@ func (a *Alfred) runTask(task string, args []string, formatted bool) bool { var wg sync.WaitGroup // Do we have any tasks we need to run in parallel? - for _, taskDefinition := range a.Tasks[task].TaskGroup(a.Tasks[task].Multitask, args) { + for _, taskDefinition := range copyOfTask.TaskGroup(copyOfTask.Multitask, args) { wg.Add(1) go func(t string, args []string) { defer wg.Done() @@ -285,7 +288,7 @@ func (a *Alfred) runTask(task string, args []string, formatted bool) bool { wg.Wait() // Ok, we made it here ... Is this task a task group? - for _, taskDefinition := range a.Tasks[task].TaskGroup(a.Tasks[task].Tasks, args) { + for _, taskDefinition := range copyOfTask.TaskGroup(copyOfTask.Tasks, args) { if !a.runTask(taskDefinition.Name, taskDefinition.Params, formatted) { break } @@ -293,7 +296,7 @@ func (a *Alfred) runTask(task string, args []string, formatted bool) bool { // Woot! Lets run the ok tasks if taskok { - for _, taskDefinition := range a.Tasks[task].TaskGroup(a.Tasks[task].Ok, args) { + for _, taskDefinition := range copyOfTask.TaskGroup(copyOfTask.Ok, args) { if !a.runTask(taskDefinition.Name, taskDefinition.Params, formatted) { break } @@ -302,8 +305,8 @@ func (a *Alfred) runTask(task string, args []string, formatted bool) bool { } // Do we need to break or should we keep going? - if a.Tasks[task].Every != "" { - if everyDuration, everyErr := time.ParseDuration(a.Tasks[task].Every); everyErr == nil { + if copyOfTask.Every != "" { + if everyDuration, everyErr := time.ParseDuration(copyOfTask.Every); everyErr == nil { <-time.After(everyDuration) } else { break diff --git a/alfred_test.go b/alfred_test.go index 1b51751..0c959d9 100644 --- a/alfred_test.go +++ b/alfred_test.go @@ -125,8 +125,8 @@ func TestAlias(t *testing.T) { } /* Make sure the ls command ran */ - if !strings.Contains(sut, "alfred.yml") { - t.Logf("ls should contain alfred.yml") + if !strings.Contains(sut, "emptydir") { + t.Logf("ls should contain emptydir") t.FailNow() } @@ -255,7 +255,7 @@ func TestArgumentsOkAndDefaults(t *testing.T) { t.FailNow() } /* Verfify our command had run succesfully */ - if !strings.Contains(sut, ".alfred") { + if !strings.Contains(sut, "emptydir") { t.Logf("Listing of the current directory should show an alfred.yml file") t.FailNow() } @@ -281,11 +281,9 @@ func TestRetryLogic(t *testing.T) { sut, _ := run("alfred twentyseven", t) /* Verfify our retry logic via error message */ - if !strings.Contains(sut, "[twentyseven] Retry logic. Try X times before continuing on\nls: /step27-idonotexist: No such file or directory\nls: /step27-idonotexist: No such file or directory\nls: /step27-idonotexist: No such file or directory") { - if !strings.Contains(sut, "[twentyseven] Retry logic. Try X times before continuing on\nls: cannot access /step27-idonotexist: No such file or directory\nls: cannot access /step27-idonotexist: No such file or directory\nls: cannot access /step27-idonotexist: No such file or directory") { - t.Logf("Expected Retry logic 3 times") - t.FailNow() - } + if strings.Count(sut, "/step27-idonotexist") != 3 { + t.Logf("Expected Retry logic 3 times") + t.FailNow() } } @@ -311,13 +309,29 @@ func TestCleanedArgs(t *testing.T) { } } -func TestExample(t *testing.T) { - sut, _ := run("alfred", t) - if len(sut) >= 0 { +func TestTaskMultiArguments(t *testing.T) { + sut, _ := run("alfred thirty.eight", t) + + if !strings.Contains(sut, "-kc-merrill-") { + t.Logf("Expecting -kc-merrill- in command output") + t.FailNow() + } + if !strings.Contains(sut, "-bruce-wayne-") { + t.Logf("Expecting -bruce-wayne- in command output") + t.FailNow() } - if len(sut) >= 0 { + if !strings.Contains(sut, "-clark-kent-") { + t.Logf("Expecting -clark-kent- in command output") + t.FailNow() + } +} +func TestExample(t *testing.T) { + sut, _ := run("alfred", t) + if len(sut) <= 0 { + t.Logf("Test example failed ... meaning no data :(") + t.FailNow() } }