-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsync.go
167 lines (134 loc) · 3.82 KB
/
sync.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
package ian
import (
"bytes"
"fmt"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/BurntSushi/toml"
)
const CooldownJournalFilename string = ".cooldown-journal.toml"
type SyncEventType int
const (
SyncEventPing SyncEventType = 1 << iota
SyncEventCreate
SyncEventUpdate
SyncEventDelete
)
type SyncEvent struct {
Type SyncEventType
Files []string
Message string
}
type SyncCooldownInfo struct {
Cooldowns map[string]time.Time
}
// Sync is called whenever changes are made to event(s), with the changes occuring in action, and calls any configured commands.
func (instance *Instance) Sync(action func() error, eventInfo SyncEvent, ignoreCooldowns bool, stdouterr io.Writer) error {
hooks := map[string]Hook{}
var cooldownJournal *SyncCooldownInfo
var isJournalChanged bool
for name, hook := range instance.Config.Hooks {
if hook.Type == 0 || hook.Type&eventInfo.Type != 0 { // Type match
ready := true
if hook.Cooldown_ != 0 && !ignoreCooldowns {
if cooldownJournal == nil {
buf, err := os.ReadFile(filepath.Join(instance.Root, CooldownJournalFilename))
if err != nil && !os.IsNotExist(err) {
return err
}
if _, err := toml.Decode(string(buf), &cooldownJournal); err != nil {
return err
}
if cooldownJournal.Cooldowns == nil {
cooldownJournal.Cooldowns = map[string]time.Time{}
}
}
// Don't need to check if map item exists with 'ok' because if it doesn't, lastChange will be 0 and it will work anyway.
lastChange := cooldownJournal.Cooldowns[name]
if now := time.Now(); lastChange.Add(hook.Cooldown_).Before(now) {
// Cooldown gone
cooldownJournal.Cooldowns[name] = now
isJournalChanged = true
} else {
// Still in cooldown
ready = false
}
}
if ready {
hooks[name] = hook
}
}
}
// PRE
for name, hook := range hooks {
if hook.PreCommand != "" {
if stdouterr != nil {
stdouterr.Write([]byte(fmt.Sprintf("\033[2m=== RUN hook '%s' PRE-command\033[22m\n", name)))
}
err := runHookCommand(eventInfo, hook.PreCommand, instance.Root, stdouterr)
if err != nil {
log.Printf("warning: sync hook command '%s' exited unsuccessfully (%s).\n", name, err)
}
if stdouterr != nil {
stdouterr.Write([]byte(fmt.Sprintf("\033[2m=== DONE hook '%s' PRE-command\033[22m\n", name)))
}
}
}
if stdouterr != nil {
stdouterr.Write([]byte("\n\033[2m=== MODIFYING EVENTS\033[0m\n\n"))
}
if err := action(); err != nil {
return err
}
// POST
for name, hook := range hooks {
if hook.PostCommand != "" {
if stdouterr != nil {
stdouterr.Write([]byte(fmt.Sprintf("\033[2m=== RUN hook '%s' POST-command\033[22m\n", name)))
}
err := runHookCommand(eventInfo, hook.PostCommand, instance.Root, stdouterr)
if err != nil {
log.Printf("warning: sync hook command '%s' exited unsuccessfully (%s).\n", name, err)
}
if stdouterr != nil {
stdouterr.Write([]byte(fmt.Sprintf("\033[2m=== DONE hook '%s' POST-command\033[22m\n", name)))
}
}
}
if isJournalChanged {
buf := new(bytes.Buffer)
if err := toml.NewEncoder(buf).Encode(cooldownJournal); err != nil {
return err
}
os.WriteFile(filepath.Join(instance.Root, CooldownJournalFilename), buf.Bytes(), 0644)
}
return nil
}
func runHookCommand(eventInfo SyncEvent, command string, workingDir string, stdouterr io.Writer) error {
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command(command)
default:
cmd = exec.Command("sh", "-c", command)
}
cmd.Stdout = stdouterr
cmd.Stderr = stdouterr
absRoot, err := filepath.Abs(workingDir)
if err != nil {
return err
}
cmd.Dir = absRoot
cmd.Env = append(os.Environ(),
"MESSAGE="+eventInfo.Message,
"FILES="+strings.Join(eventInfo.Files, " "),
"TYPE="+fmt.Sprint(eventInfo.Type),
)
return cmd.Run()
}