forked from go-godo/godo
/
task.go
168 lines (148 loc) · 3.9 KB
/
task.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package godo
import (
"fmt"
"os"
"path/filepath"
"time"
"gopkg.in/godo.v1/util"
"gopkg.in/godo.v1/watcher"
)
// TaskFunction is the signature of the function used to define a type.
// type TaskFunc func(string, ...interface{}) *Task
// type UseFunc func(string, interface{})
// A Task is an operation performed on a user's project directory.
type Task struct {
Name string
description string
Dependencies []string
Handler Handler
// Watches are the files are watched. On change the task is rerun. For example `**/*.less`
// Usually Watches and Sources are the same.
WatchFiles []*FileAsset
WatchGlobs []string
WatchRegexps []*RegexpInfo
// computed based on dependencies
EffectiveWatchRegexps []*RegexpInfo
EffectiveWatchGlobs []string
// Complete indicates whether this task has already ran. This flag is
// ignored in watch mode.
Complete bool
debounce int64
RunOnce bool
}
// Expands glob patterns.
func (task *Task) expandGlobs() {
files, regexps, err := Glob(task.WatchGlobs)
if err != nil {
util.Error(task.Name, "%v", err)
return
}
task.WatchRegexps = regexps
task.WatchFiles = files
}
// Run runs all the dependencies of this task and when they have completed,
// runs this task.
func (task *Task) Run() {
if !watching && task.Complete {
util.Debug(task.Name, "Already ran\n")
return
}
task.RunWithEvent(task.Name, nil)
}
// isWatchedFile determines if a FileEvent's file is a watched file
func (task *Task) isWatchedFile(e *watcher.FileEvent) bool {
cwd, err := os.Getwd()
if err != nil {
return false
}
filename, err := filepath.Rel(cwd, e.Path)
filename = filepath.ToSlash(filename)
//util.Debug("task", "checking for match %s\n", filename)
if err != nil {
return false
}
matched := false
for _, info := range task.EffectiveWatchRegexps {
if info.Negate {
if matched {
matched = !info.MatchString(filename)
//util.Debug("task", "negated match? %s %s\n", filename, matched)
continue
}
} else if info.MatchString(filename) {
matched = true
//util.Debug("task", "matched %s %s\n", filename, matched)
continue
}
}
return matched
}
// RunWithEvent runs this task when triggered from a watch.
// *e* FileEvent contains information about the file/directory which changed
// in watch mode.
func (task *Task) RunWithEvent(logName string, e *watcher.FileEvent) error {
if task.RunOnce && task.Complete {
//util.Debug(task.Name, "Already ran\n")
return nil
}
start := time.Now()
if len(task.WatchGlobs) > 0 && len(task.WatchFiles) == 0 {
task.expandGlobs()
if len(task.WatchFiles) == 0 {
util.Error("task", "\""+task.Name+"\" '%v' did not match any files\n", task.WatchGlobs)
}
}
// Run this task only if the file matches watch Regexps
rebuilt := ""
if e != nil {
rebuilt = "rebuilt "
if !task.isWatchedFile(e) {
return nil
}
if verbose {
util.Debug(logName, "%s\n", e.String())
}
}
var err error
log := true
if task.Handler != nil {
context := Context{Task: task, Args: contextArgm}
err = task.Handler.Handle(&context)
if err != nil {
return fmt.Errorf("%q: %s", logName, err.Error())
}
} else if len(task.Dependencies) > 0 {
// no need to log if just dependency
log = false
} else {
util.Info(task.Name, "Ignored. Task does not have a handler or dependencies.\n")
return nil
}
elapsed := time.Now().Sub(start)
if log {
util.Info(logName, "%s%vms\n", rebuilt, elapsed.Nanoseconds()/1e6)
}
task.Complete = true
return nil
}
// Debounce is minimum milliseconds before task can run again
func (task *Task) Debounce(ms int64) *Task {
if ms > 0 {
task.debounce = ms
}
return task
}
// Watch a set of glob file patterns.
func (task *Task) Watch(globs ...string) *Task {
if len(globs) > 0 {
task.WatchGlobs = globs
}
return task
}
// Description sets the description for the task.
func (task *Task) Description(desc string) *Task {
if desc != "" {
task.description = desc
}
return task
}