golang build tool in the spirt of rake, gulp
Clone or download
Mario Gutierrez
Mario Gutierrez Merge pull request #51 from agonzalezro/patch-1
Add proper link to godoc documentation
Latest commit b0de4ae Nov 29, 2016
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
Gododir use testify.v1 Jun 22, 2015
book use testify.v1 Jun 22, 2015
cmd optimize watching Apr 14, 2016
glob first v2 drop Apr 11, 2015
test first v2 drop Apr 11, 2015
util optimize watching Apr 14, 2016
watcher optimize watching Apr 14, 2016
.gitignore Fix bug with runonce syntax : task not found Sep 14, 2015
CHANGES-V2.md update changelog Jan 14, 2016
LICENSE add LICENSE; lint Oct 27, 2014
Makefile optimize watching Apr 14, 2016
README.md Add proper link to godoc documentation Nov 29, 2016
VERSION.go optimize watching Apr 14, 2016
cmd.go Context.Start: improve rebuild time on watch by building changed file… Dec 23, 2015
context.go Context.Start: improve rebuild time on watch by building changed file… Dec 23, 2015
doc.go first v2 drop Apr 11, 2015
env.go process godoenv file; revert child process killing due to instability Dec 8, 2015
env_test.go process godoenv file; revert child process killing due to instability Dec 8, 2015
exec.go fix #38 Jan 22, 2016
exec_test.go use testify.v1 Jun 22, 2015
fileWrapper.go first v2 drop Apr 11, 2015
handler.go first v2 drop Apr 11, 2015
init_test.go optimize watching Apr 14, 2016
namespace_test.go use testify.v1 Jun 22, 2015
project.go optimize watching Apr 14, 2016
project_test.go optimize watching Apr 14, 2016
runner.go process godoenv file; revert child process killing due to instability Dec 8, 2015
task.go optimize watching Apr 14, 2016
task_options.go first v2 drop Apr 11, 2015
waitgroup.go first v2 drop Apr 11, 2015
watch_test.go use testify.v1 Jun 22, 2015

README.md

Documentation is WIP

godo

GoDoc

godo is a task runner and file watcher for golang in the spirit of rake, gulp.

To install

go get -u gopkg.in/godo.v2/cmd/godo

Godofile

Godo runs Gododir/main.go.

As an example, create a file Gododir/main.go with this content

package main

import (
    "fmt"
    do "gopkg.in/godo.v2"
)

func tasks(p *do.Project) {
    do.Env = `GOPATH=.vendor::$GOPATH`

    p.Task("default", do.S{"hello", "build"}, nil)

    p.Task("hello", nil, func(c *do.Context) {
        name := c.Args.AsString("name", "n")
        if name == "" {
            c.Bash("echo Hello $USER!")
        } else {
            fmt.Println("Hello", name)
        }
    })

    p.Task("assets?", nil,  func(c *do.Context) {
        // The "?" tells Godo to run this task ONLY ONCE regardless of
        // how many tasks depend on it. In this case watchify watches
        // on its own.
	    c.Run("watchify public/js/index.js d -o dist/js/app.bundle.js")
    }).Src("public/**/*.{css,js,html}")

    p.Task("build", do.S{"views", "assets"}, func(c *do.Context) {
        c.Run("GOOS=linux GOARCH=amd64 go build", do.M{"$in": "cmd/server"})
    }).Src("**/*.go")

    p.Task("server", do.S{"views", "assets"}, func(c *do.Context) {
        // rebuilds and restarts when a watched file changes
        c.Start("main.go", do.M{"$in": "cmd/server"})
    }).Src("server/**/*.go", "cmd/server/*.{go,json}").
       Debounce(3000)

    p.Task("views", nil, func(c *do.Context) {
        c.Run("razor templates")
    }).Src("templates/**/*.go.html")
}

func main() {
    do.Godo(tasks)
}

To run "server" task from parent dir of Gododir/

godo server

To rerun "server" and its dependencies whenever any of their watched files change

godo server --watch

To run the "default" task which runs "hello" and "build"

godo

Task names may add a "?" suffix to execute only once even when watching

// build once regardless of number of dependents
p.Task("assets?", nil, func(*do.Context) { })

Task dependencies

do.S{} or do.Series{} - dependent tasks to run in series
do.P{} or do.Parallel{} - dependent tasks to run in parallel

For example, do.S{"clean", do.P{"stylesheets", "templates"}, "build"}

Task Option Funcs

  • Task#Src() - specify watch paths or the src files for Task#Dest()

    Glob patterns
    
        /**/   - match zero or more directories
        {a,b}  - match a or b, no spaces
        *      - match any non-separator char
        ?      - match a single non-separator char
        **/    - match any directory, start of pattern only
        /**    - match any in this directory, end of pattern only
        !      - removes files from result set, start of pattern only
    
  • Task#Dest(globs ...string) - If globs in Src are newer than Dest, then the task is run

  • Task#Desc(description string) - Set task's description in usage.

  • Task#Debounce(duration time.Duration) - Disallow a task from running until duration has elapsed.

  • Task#Deps(names ...interface{}) - Can be S, Series, P, Parallel, string

Task CLI Arguments

Task CLI arguments follow POSIX style flag convention (unlike go's built-in flag package). Any command line arguments succeeding -- are passed to each task. Note, arguments before -- are reserved for godo.

As an example,

p.Task("hello", nil, func(c *do.Context) {
    // "(none)" is the default value
    msg := c.Args.MayString("(none)", "message", "msg", "m")
    var name string
    if len(c.Args.NonFlags()) == 1 {
        name = c.Args.NonFlags()[0]
    }
    fmt.Println(msg, name)
})

running

# prints "(none)"
godo hello

# prints "Hello dude" using POSIX style flags
godo hello -- dude --message Hello
godo hello -- dude --msg Hello
godo hello -- -m Hello dude

Args functions are categorized as

  • Must* - Argument must be set by user or panic.

c.Args.MustInt("number", "n")


*  `May*` - If argument is not set, default to first value.

    ```go
// defaults to 100
c.Args.MayInt(100, "number", "n")
  • As* - If argument is not set, default to zero value.

// defaults to 0 c.Args.AsInt("number", "n")



## Modularity and Namespaces

A project may include other tasks functions with `Project#Use`. `Use` requires a namespace to
prevent task name conflicts with existing tasks.

```go
func buildTasks(p *do.Project) {
    p.Task("default", S{"clean"}, nil)

    p.Task("clean", nil, func(*do.Context) {
        fmt.Println("build clean")
    })
}

func tasks(p *do.Project) {
    p.Use("build", buildTasks)

    p.Task("clean", nil, func(*do.Context) {
        fmt.Println("root clean")
    })

    p.Task("build", do.S{"build:default"}, func(*do.Context) {
        fmt.Println("root clean")
    })
}

Running godo build:. or godo build results in output of build clean. Note that it uses the clean task in its namespace not the clean in the parent project.

The special name build:. is alias for build:default.

Task dependencies that start with "/" are relative to the parent project and may be called referenced from sub projects.

godobin

godo compiles Godofile.go to godobin-VERSION (godobin-VERSION.exe on Windows) whenever Godofile.go changes. The binary file is built into the same directory as Godofile.go and should be ignored by adding the path godobin* to .gitignore.

Exec functions

All of these functions accept a map[string]interface{} or M for options. Option keys that start with "$" are reserved for godo. Other fields can be used as context for template.

Bash

Bash functions uses the bash executable and may not run on all OS.

Run a bash script string. The script can be multiline line with continutation.

c.Bash(`
    echo -n $USER
    echo some really long \
        command
`)

Bash can use Go templates

c.Bash(`echo -n {{.name}}`, do.M{"name": "mario", "$in": "cmd/bar"})

Run a bash script and capture STDOUT and STDERR.

output, err := c.BashOutput(`echo -n $USER`)

Run

Run go build inside of cmd/app and set environment variables.

c.Run(`GOOS=linux GOARCH=amd64 go build`, do.M{"$in": "cmd/app"})

Run can use Go templates

c.Run(`echo -n {{.name}}`, do.M{"name": "mario", "$in": "cmd/app"})

Run and capture STDOUT and STDERR

output, err := c.RunOutput("whoami")

Start

Start an async command. If the executable has suffix ".go" then it will be "go install"ed then executed. Use this for watching a server task.

c.Start("main.go", do.M{"$in": "cmd/app"})

Godo tracks the process ID of started processes to restart the app gracefully.

Inside

To run many commands inside a directory, use Inside instead of the $in option. Inside changes the working directory.

do.Inside("somedir", func() {
    do.Run("...")
    do.Bash("...")
})

User Input

To get plain string

user := do.Prompt("user: ")

To get password

password := do.PromptPassword("password: ")

Godofile Run-Time Environment

From command-line

Environment variables may be set via key-value pairs as arguments to godo. This feature was added to facilitate users on Windows.

godo NAME=mario GOPATH=./vendor hello

From source code

To specify whether to inherit from parent's process environment, set InheritParentEnv. This setting defaults to true

do.InheritParentEnv = false

To specify the base environment for your tasks, set Env. Separate with whitespace or newlines.

do.Env = `
    GOPATH=.vendor::$GOPATH
    PG_USER=mario
`

Functions can add or override environment variables as part of the command string. Note that environment variables are set before the executable similar to a shell; however, the Run and Start functions do not use a shell.

p.Task("build", nil, func(c *do.Context) {
    c.Run("GOOS=linux GOARCH=amd64 go build" )
})

The effective environment for exec functions is: parent (if inherited) <- do.Env <- func parsed env

Paths should use :: as a cross-platform path list separator. On Windows :: is replaced with ;. On Mac and linux :: is replaced with :.

From godoenv file

For special circumstances where the GOPATH needs to be set before building the Gododir, use Gododir/godoenv file.

TIP: Create Gododir/godoenv when using a dependency manager like godep that necessitates changing $GOPATH

# Gododir/godoenv
GOPATH=$PWD/cmd/app/Godeps/_workspace::$GOPATH