This repository has been archived by the owner on Jul 25, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathtask.go
129 lines (113 loc) · 2.83 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
package urknall
import (
"fmt"
"log"
"runtime/debug"
"time"
"github.com/dynport/urknall/cmd"
"github.com/dynport/urknall/pubsub"
)
// A task is a list of commands. Each task is cached internally, i.e. if an
// command has been executed already, none of the preceding tasks has changed
// and neither the command itself, then it won't be executed again. This
// enhances performance and removes the burden of writing idempotent commands.
type Task interface {
Add(cmds ...interface{}) Task
Commands() ([]cmd.Command, error)
}
// Create a task. This is available to provide maximum flexibility, but
// shouldn't be required very often. The resulting task can be registered to an
// package using the AddTask method.
func NewTask() Task {
return &task{}
}
type task struct {
commands []*commandWrapper
name string // Name of the compilable.
taskBuilder Template // only used for rendering templates TODO(gf): rename
compiled bool
validated bool
started time.Time // time used to for caching timestamp
}
func (t *task) Commands() (cmds []cmd.Command, e error) {
if e = t.Compile(); e != nil {
return nil, e
}
for _, c := range t.commands {
cmds = append(cmds, c.command)
}
return cmds, nil
}
func (task *task) Add(cmds ...interface{}) Task {
for _, c := range cmds {
switch t := c.(type) {
case string:
// No explicit expansion required as the function is called recursively with a ShellCommand type, that has
// explicitly renders the template.
task.addCommand(&stringCommand{cmd: t})
case cmd.Command:
task.addCommand(t)
default:
panic(fmt.Sprintf("type %T not supported!", t))
}
}
return task
}
func (task *task) validate() error {
if !task.validated {
if task.taskBuilder == nil {
return nil
}
e := validateTemplate(task.taskBuilder)
if e != nil {
return e
}
task.validated = true
}
return nil
}
func (task *task) addCommand(c cmd.Command) {
if task.taskBuilder != nil {
e := task.validate()
if e != nil {
panic(e.Error())
}
if renderer, ok := c.(cmd.Renderer); ok {
renderer.Render(task.taskBuilder)
}
if validator, ok := c.(cmd.Validator); ok {
if e := validator.Validate(); e != nil {
panic(e.Error())
}
}
}
task.commands = append(task.commands, &commandWrapper{command: c})
}
func (task *task) Compile() (e error) {
if task.compiled {
return nil
}
m := message(pubsub.MessageTasksPrecompile, "", task.name)
m.Publish("started")
defer func() {
if r := recover(); r != nil {
var ok bool
e, ok = r.(error)
if !ok {
e = fmt.Errorf("failed to precompile package: %v %q", task.name, r)
}
m.Error = e
m.Stack = string(debug.Stack())
m.Publish("panic")
log.Printf("ERROR: %s", r)
log.Print(string(debug.Stack()))
}
}()
e = task.validate()
if e != nil {
return e
}
m.Publish("finished")
task.compiled = true
return nil
}