{"id":13393354,"url":"https://github.com/dshills/goauto","last_synced_at":"2026-01-18T02:41:31.626Z","repository":{"id":29615898,"uuid":"33156412","full_name":"dshills/goauto","owner":"dshills","description":"Go package for building automation tools","archived":false,"fork":false,"pushed_at":"2015-11-29T15:17:45.000Z","size":88,"stargazers_count":269,"open_issues_count":0,"forks_count":15,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-03-13T19:38:57.707Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dshills.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-03-31T00:49:51.000Z","updated_at":"2024-07-19T02:57:31.000Z","dependencies_parsed_at":"2022-07-27T22:19:29.970Z","dependency_job_id":null,"html_url":"https://github.com/dshills/goauto","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/dshills/goauto","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dshills%2Fgoauto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dshills%2Fgoauto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dshills%2Fgoauto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dshills%2Fgoauto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dshills","download_url":"https://codeload.github.com/dshills/goauto/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dshills%2Fgoauto/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28526734,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T17:00:51.004Z","updated_at":"2026-01-18T02:41:31.594Z","avatar_url":"https://github.com/dshills.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# GoAuto [![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/dshills/goauto)\n\u003e\"What makes you so ashamed of being a grownup?\" - The War Doctor\n\n## Overview\nTask automation for grownups. GoAuto is a package that makes building a native executable tailored to a specific work flow, simple. \n\nHere is a complete example of a Go build and test process triggered by any source file in a project changing. \n\n**NOTE** If you are using OS X consider turning on the experimental OSX flag for Pipelines to avoid the \"To many Open Files\" error.\n\n```go\npackage main\n\nimport (\n\t\"path/filepath\"\n\n\t\"github.com/dshills/goauto\"\n\t\"github.com/dshills/goauto/gotask\"\n)\n\nfunc main() {\n\t// Create a pipeline (Develop using Verbose, Change to Silent after testing)\n\tp := goauto.NewPipeline(\"Go Pipeline\", goauto.Verbose)\n\tdefer p.Stop()\n\n\t// Watch directories recursively, ignoring hidden directories\n\twd := filepath.Join(\"src\", \"github.com\", \"me\", \"myproject\")\n\tif err := p.WatchRecursive(wd, goauto.IgnoreHidden); err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create a workflow\n\twf := goauto.NewWorkflow(\n\t\t\t\t\tgotask.NewGoVetTask(), \n\t\t\t\t\tgotask.NewGoLintTask(), \n\t\t\t\t\tgotask.NewGoTestTask(),\n\t\t\t\t\tgotask.NewGoInstallTask())\n\n\t// Add a file pattern to match\n\tif err := wf.WatchPattern(\".*\\\\.go$\"); err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Add workflow to pipeline\n\tp.Add(wf)\n\n\t// start the pipeline, it will block\n\tp.Start()\n}\n```\n\n## Features\n* No config files\n* Built in support for Go projects\n* No new syntax just Go\n* No external dependencies\n* Highly customizable\n* Fast\n* Small\n* Tool for building tools\n\nBuilding a general purpose build tool with GoAuto that used config files would be a fairly trivial project. Feel free if that is your thing.\n\n\n## Installation\n\tgo get github.com/dshills/goauto\n\t\n## Concepts\n\n### Pipelines\nA Pipeline monitors one or more file system directories for changes. When it detects a change it asks each Workflow if the specific file is a match and if it is launches the Workflow. Output and Error io can be set for a Pipeline. If not specified it will use StdIn and StdErr. One or more Pipelines can be declared. Running them concurrently is a choice left to the developer.\n\nGoAuto follows the go tools convention of no news is good news. It will silently watch for file changes, launch workflows and tasks without any output other than the output from the task itself. If running a task like a go tool that has the same philosophy, no output will be generated at all. When first writing a set of tasks this can be a little disconcerting. Did it run? Did it work?\n\n```go\ngoauto.Verbose\ngoauto.Silent\n```\n\nVerbose will print debug information about what events are being received by a Pipeline, starting a workflow, and tasks being run. At some point an option may be added to just show specific output. When writing your own tasks you always have the choice of outputting whatever you wish.\n\nWatches can be absolute or $GOPATH relative.\n\n```go\n// Create a pipeline\np := goauto.NewPipeline(\"Go Pipeline\", goauto.Verbose)\n\n// watch directories recursively, ignoring hidden directories\nwd := filepath.Join(\"src\", \"github.com\", \"myprojects\")\nif err := p.WatchRecursive(wd, goauto.IgnoreHidden); err != nil {\n\tpanic(err)\n}\n```\n\nWatch directories can be added with Watch to add a single directory. The absolute path of the added path will be returned.\n\n\tfunc (p *Pipeline) Watch(watchDir string) (string, error)\n\t\nTo add directories recursively use WatchRecursive optionally ignoring hidden directories\n\n\tfunc (p *Pipeline) WatchRecursive(watchDir string, ignoreHidden bool) error \n\nAdding Workflows are added using Add\n\n\tfunc (p *Pipeline) Add(ws ...Workflower)\n\nAfter adding Workflows and Tasks to your pipeline simply tell the Pipeline to Start watching. The Pipeline will block. To stop a running Pipeline call Stop.\n\n```go\np.Start()\n\nOr\n\ngo p.Start()\n\n\tdo stuff\n\np.Stop()\n```\n\n**UPDATE** Pipeline now includes an experimental flag OSX. If you are using OS X and have received the \"To many open files\" warning this is an attempt to fix it. The watcher code has now been extracted into it's own interface and can use the experimental OSX events package https://github.com/go-fsnotify/fsevents. I have done heavy testing locally with no problems but your mileage may vary. This should have no affect on the current usage of GoAuto.\n\n\n### Workflows\n\nWorkflows run a set of tasks for files matching a regular expression pattern.  Workflows only really need to know two things, what files to process and what tasks to perform. Workflow implements the Workflower interface.\n\nHere we create a Workflow with a number of built in tasks. We then add a pattern to match using WatchPattern.\n\n```go\n// Create a workflow with some tasks\nwf := goauto.NewWorkflow(goauto.NewGoVetTask(), goauto.NewGoTestTask(), goauto.NewGoLintTask(), goauto.NewGoInstallTask())\n\n// Add a regex pattern to match\nerr := wf.WatchPattern(\".*\\\\.go$\")\n```\nTasks can also be added using Add\n\n\tfunc (wf *Workflow) Add(tasks ...Tasker)\n\nWorkflows run tasks sequentially, passing the TaskInfo struct (See Tasks below) to each task on the way. Before a task is run the TaskInfo.Src is updated to the TaskInfo.Target of the previous task if it was set. TaskInfo.Src is set to the matching file name for the first task. \n\nAny task that returns an error will stop the Workflow. If you want the Workflow to continue even if an error occurs make sure to handle the error and not return it.\n\n#### Advanced Options\n\n```go\n// A Workflow represents a set of tasks for files matching one or more regex patterns\ntype Workflow struct {\n\tName       string\n\tConcurrent bool\n\tOp         Op\n\tRegexs     []*regexp.Regexp\n\tTasks      []Tasker\n}\n```\n\nSetting Concurrent to true will run a Workflow concurrently. This should be used with caution. If multiple Workflows work with the same set of files there is a potential for confusion and even data loss.\n\n\tOp = goauto.Create | goauto.Write | goauto.Remove | goauto.Rename | goauto.Chmod\n\nBy default a Workflow will check file match for Create, Write, Remove, and Rename. This can be controlled by setting the Op value.\n\nThe Workflow struct implements the Workflower interface. Most use cases will have no need for anything more than a Workflow, however, Pipelines will accept anything that implements the Workflower interface. An example might be a new Workflower that implemented the WatchPattern using glob syntax rather than a regex. \n\n### Tasks\n\nTasks are generally small, atomic pieces of work. Run tests, compile, copy a file, etc. They are what makeup a Workflow. A task implements the Tasker interface\n\n#### Task Builtins\nGoAuto includes a number of pre built tasks that can be used directly.\n\n##### goauto/gotask\n\n* NewGoPrjTask will run a go command with arguments\n* NewGoTestTask will run tests for a project\n* NewGoVetTask will run vet for a project\n* NewGoBuildTask will run build for a project\n* NewGoInstallTask will run install for a project\n* NewGoLintTask will run golint for a project\n* NewGoMetaLinter task for github.com/alecthomas/gometalinter\n\n##### goauto/shelltask\n\n* NewShellTask task that runs a shell command with arguments\n* NewCatTask task that cats a file\n* NewRemoveTask task that deleted a file\n* NewMoveTask task that moves a file\n* NewMkdirTask task that makes a new directory\n* NewCopyTask task that copies a file\n* NewRestartTask task that will restart an executable file such as a Go server or Web server\n\n##### goauto/webtask\n\n* NewSassTask task that runs sass command line utility with options\n\n#### Task Generators\nThe built in tasks are a great way to get started with GoAuto. They do many useful things and serve as guides for building your own tasks. GoAuto also includes generator functions that will help you build your own simple tasks. NewTask, NewShellTask and NewGoPrjTask are examples of generic task generators.\n\nNewTask is the most generic of the generators and can be used for building your own task. NewTask usage will be covered in Task Building\n\nNewShellTask will return a new Tasker that will call a shell function with arguments on the target file. \n\n\tfunc NewShellTask(cmd string, args ...string) Tasker\n\n\tst := shelltask.NewShellTask(\"echo\", \"-n\") // a Task that will echo the source file name\n\n\nNewGoPrjTask will produce a new Tasker that will call the go command in the directory of target file\n\n\tfunc NewGoPrjTask(gocmd string, args ...string) Tasker \n\n\tgt := gotask.NewGoPrjTask('build') // A fancy new task that will build a project\n\n#### TaskInfo\nBefore diving into task building we need to introduce the TaskInfo struct. TaskInfo is passed between tasks as they run. \n\n```go\n// A TaskInfo contains the results of running a Task\ntype TaskInfo struct {\n\tSrc        string       // Incoming file name for a task to process\n\tTarget     string       // Output file name after running a task\n\tBuf        bytes.Buffer // Output of running a task\n\tTout, Terr io.Writer    // Writers to write output and errors\n\tCollect    []string     // List of file names processed by a Workflow\n\tVerbose    bool         // output debug info\n}\n```\n\nYour tasks are expected to update Target and Buf and to use Tout and Terr for output. For example a task that renames a file would set Target equal to the new file name. If your task has output useful to another task then reset the Buf and write it. User messages or error text can be written to Tout and Terr. \n\nAs the Workflow executes each task the Src will be set to the Target of the last run task. For the first task in a Workflow Src is set to the filename matched by the Workflow.\n\nBy using Buf and Target a Workflow creates a flow similar to a using a Unix pipe \n\nCollect keeps a running list of file targets over the course of one run of a Workflow. This gives tasks access to run functions on all the files processed by the Workflow.\n\n#### Task Building\nThe real power comes from building custom tasks. This can be done using the NewTask generator or by writing a Tasker compliant interface. Here are examples of both for calling the cat shell command.\n\n\tfunc NewTask(t Transformer, r Runner) Tasker \n\nA Transformer is the function used to convert the incoming file name to something new. A number of Transformers are built in.\n\n\tfunc(string)string\n\n##### Built In Transformers\n\n* Identity function that returns the string passed in \n* GoRelBase function that returns the file path relative to GOPATH \n* GoRelDir function that returns the directory path relative to GOPATH\n* GoRelSrcDir function that returns the directory path relative to GOPATH/src\n* ExtTransformer function that returns a Transformer function that returns file path with a new extension\n\nA runner is the function called to run your task and is in the form\n\n\tfunc foo(i *TaskInfo)error\n\nSo lets write a task that cats a file. A cat task is already included but it is a simple example. We are using the Identity Transformer which just returns the file name passed to it. \n\n```go\nfunc myCat(i *goauto.TaskInfo) (err error) {\n\tcmd := exec.Command(\"cat\", i.Target)\n\ti.Buf.Reset()\n\tcmd.Stdout = \u0026i.Buf // Write the output to the buffer\n\tcmd.Stderr = i.Terr // use Terr as stderr\n\tdefer func() {\n\t\ti.Tout.Write(i.Buf.Bytes()) // Write the buffer to Tout\n\t}()\n\treturn cmd.Run()\n}\n\nt := goauto.NewTask(goauto.Identity, myCat)\n// Identity is a built in Transformer that returns what was passed to it\n// We could have written goauto.NewTask(func(f string)string {return f}, myCat)\n```\n\nHere it is written as a Tasker. In this case we don't need a Transformer because we are controlling the entire task from start to finish. In this simple example it is actually shorter to make our own Tasker\n\n```go\ntype myCatTask struct{}\nfunc (t *myCatTask)Run(i *goauto.TaskInfo) (err error) {\n\ti.Target = i.Src // Not changing the file name so not technically required but a good habit\n\tcmd := exec.Command(\"cat\", i.Target)\n\ti.Buf.Reset()\n\tcmd.Stdout = \u0026i.Buf // Write the output to the buffer\n\tcmd.Stderr = i.Terr // use Terr as stderr\n\tdefer func() {\n\t\ti.Tout.Write(i.Buf.Bytes()) // Write the buffer to Tout\n\t}()\n\treturn cmd.Run()\n}\n```\n\n## To Do\n* More built ins for Web development LESS, Reload (Certainly can be done now but it would be nice to have built ins)\n* Test large, concurrent, multi Pipeline, multi Workflow systems\n\n## Alternatives\n\n* [Slurp](https://github.com/omeid/slurp) Go\n* [Gulp](http://gulpjs.com/) Node.js\n* [Grunt](http://gruntjs.com/) Node.js\n\n## License\nCopyright 2015 Davin Hills. All rights reserved.\nMIT license. License details can be found in the LICENSE file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdshills%2Fgoauto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdshills%2Fgoauto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdshills%2Fgoauto/lists"}