Skip to content

Commit

Permalink
Merge pull request kcmerrill#28 from kcmerrill/kc/taskparams
Browse files Browse the repository at this point in the history
Added task(params) for all task groups
  • Loading branch information
kcmerrill authored Mar 4, 2017
2 parents 26c9310 + 01a8165 commit 7e0dc85
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 131 deletions.
40 changes: 26 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
# 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)

![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:
Expand All @@ -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
Expand Down Expand Up @@ -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
File renamed without changes.
1 change: 1 addition & 0 deletions alfred.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ tdd:
test:
summary: Testing ...
command: |
go install
go test -v
commit:
Expand Down
88 changes: 46 additions & 42 deletions alfred/alfred.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (

"github.com/fatih/color"
"github.com/kcmerrill/alfred/remote"
"github.com/kcmerrill/alfred/task"
"gopkg.in/yaml.v2"
)

Expand All @@ -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
Expand Down Expand Up @@ -102,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
}
Expand All @@ -112,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 task.")
say(a.args[2], "Invalid task")
return false
}
break
Expand All @@ -133,11 +134,13 @@ func (a *Alfred) runTask(task string, args []string, formatted bool) bool {
return false
}

copyOfTask := a.Tasks[task]

// 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 copyOfTask.TaskGroup(copyOfTask.Setup, args) {
if !a.runTask(taskDefinition.Name, taskDefinition.Params, formatted) {
break
}
}
Expand All @@ -151,33 +154,34 @@ 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("")
return errors.New("no matches")
}
}
return nil
Expand All @@ -192,25 +196,25 @@ 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
}
}

// 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
}
Expand All @@ -222,22 +226,22 @@ 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
}
}
}

// 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)
}

Expand All @@ -248,8 +252,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 copyOfTask.TaskGroup(copyOfTask.Fail, args) {
if !a.runTask(taskDefinition.Name, taskDefinition.Params, formatted) {
break
}
}
Expand All @@ -260,49 +264,49 @@ 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
}

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 copyOfTask.TaskGroup(copyOfTask.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 copyOfTask.TaskGroup(copyOfTask.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 copyOfTask.TaskGroup(copyOfTask.Ok, args) {
if !a.runTask(taskDefinition.Name, taskDefinition.Params, formatted) {
break
}
}

}

// 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
Expand Down
Loading

0 comments on commit 7e0dc85

Please sign in to comment.