6
6
"io"
7
7
"os"
8
8
"os/exec"
9
+ "path/filepath"
9
10
"strings"
10
11
"sync"
11
12
"time"
@@ -58,6 +59,7 @@ Example use:
58
59
watch , _ = cmd .Flags ().GetBool ("watch" )
59
60
parallel , _ = cmd .Flags ().GetBool ("parallel" )
60
61
rawOutput , _ = cmd .Flags ().GetBool ("raw-output" )
62
+ cacheKey , _ = cmd .Flags ().GetString ("cache-key" )
61
63
)
62
64
63
65
ws , err := getWorkspace ()
@@ -161,7 +163,7 @@ Example use:
161
163
}
162
164
163
165
if watch {
164
- err := executeCommandInLocations (args , locs , parallel , rawOutput )
166
+ err := executeCommandInLocations (args , locs , noExecCache {}, parallel , rawOutput )
165
167
if err != nil {
166
168
log .Error (err )
167
169
}
@@ -170,7 +172,7 @@ Example use:
170
172
for {
171
173
select {
172
174
case <- evt :
173
- err := executeCommandInLocations (args , locs , parallel , rawOutput )
175
+ err := executeCommandInLocations (args , locs , noExecCache {}, parallel , rawOutput )
174
176
if err != nil {
175
177
log .Error (err )
176
178
}
@@ -179,7 +181,20 @@ Example use:
179
181
}
180
182
}
181
183
}
182
- err = executeCommandInLocations (args , locs , parallel , rawOutput )
184
+
185
+ var cache execCache = noExecCache {}
186
+ if cacheKey != "" {
187
+ localCacheLoc := os .Getenv (leeway .EnvvarCacheDir )
188
+ if localCacheLoc == "" {
189
+ localCacheLoc = filepath .Join (os .TempDir (), "cache" )
190
+ }
191
+ loc := filepath .Join (localCacheLoc , cacheKey )
192
+ log .WithField ("loc" , loc ).Debug ("using filesystem exec cache" )
193
+
194
+ cache = filesystemExecCache (loc )
195
+ }
196
+
197
+ err = executeCommandInLocations (args , locs , cache , parallel , rawOutput )
183
198
if err != nil {
184
199
log .WithError (err ).Fatal ("cannot execut command" )
185
200
}
@@ -193,9 +208,13 @@ type commandExecLocation struct {
193
208
Name string
194
209
}
195
210
196
- func executeCommandInLocations (rawExecCmd []string , locs []commandExecLocation , parallel , rawOutput bool ) error {
211
+ func executeCommandInLocations (rawExecCmd []string , locs []commandExecLocation , cache execCache , parallel , rawOutput bool ) error {
197
212
var wg sync.WaitGroup
198
213
for _ , loc := range locs {
214
+ if ok , _ := cache .NeedsExecution (context .Background (), loc .Package ); ! ok {
215
+ continue
216
+ }
217
+
199
218
execCmd := make ([]string , len (rawExecCmd ))
200
219
for i , c := range rawExecCmd {
201
220
if loc .Package == nil {
@@ -233,14 +252,24 @@ func executeCommandInLocations(rawExecCmd []string, locs []commandExecLocation,
233
252
go func (loc commandExecLocation ) {
234
253
defer wg .Done ()
235
254
236
- err = cmd .Wait ()
237
- if err != nil {
255
+ err := cmd .Wait ()
256
+ if err == nil {
257
+ err = cache .MarkExecuted (context .Background (), loc .Package )
258
+ if err != nil {
259
+ log .WithError (err ).Warn ("cannot mark package as executed" )
260
+ }
261
+ } else {
238
262
log .Errorf ("execution failed in %s (%s): %v" , loc .Name , loc .Dir , err )
239
263
}
240
264
}(loc )
241
265
} else {
242
266
err = cmd .Wait ()
243
- if err != nil {
267
+ if err == nil {
268
+ err = cache .MarkExecuted (context .Background (), loc .Package )
269
+ if err != nil {
270
+ log .WithError (err ).Warn ("cannot mark package as executed" )
271
+ }
272
+ } else {
244
273
return fmt .Errorf ("execution failed in %s (%s): %v" , loc .Name , loc .Dir , err )
245
274
}
246
275
}
@@ -265,5 +294,58 @@ func init() {
265
294
execCmd .Flags ().Bool ("watch" , false , "Watch source files and re-execute on change" )
266
295
execCmd .Flags ().Bool ("parallel" , false , "Start all executions in parallel independent of their order" )
267
296
execCmd .Flags ().Bool ("raw-output" , false , "Produce output without package prefix" )
297
+ execCmd .Flags ().String ("cache-key" , "" , "Specify a cache key to provide package-cache like execution behaviour" )
268
298
execCmd .Flags ().SetInterspersed (true )
269
299
}
300
+
301
+ type execCache interface {
302
+ NeedsExecution (ctx context.Context , pkg * leeway.Package ) (bool , error )
303
+ MarkExecuted (ctx context.Context , pkg * leeway.Package ) error
304
+ }
305
+
306
+ type noExecCache struct {}
307
+
308
+ func (noExecCache ) MarkExecuted (ctx context.Context , pkg * leeway.Package ) error { return nil }
309
+ func (noExecCache ) NeedsExecution (ctx context.Context , pkg * leeway.Package ) (bool , error ) {
310
+ return true , nil
311
+ }
312
+
313
+ type filesystemExecCache string
314
+
315
+ func (c filesystemExecCache ) MarkExecuted (ctx context.Context , pkg * leeway.Package ) error {
316
+ err := os .MkdirAll (string (c ), 0755 )
317
+ if err != nil {
318
+ return err
319
+ }
320
+ fn , err := c .filename (pkg )
321
+ if err != nil {
322
+ return err
323
+ }
324
+ f , err := os .Create (string (fn ))
325
+ if err != nil {
326
+ return err
327
+ }
328
+ f .Close ()
329
+ log .WithField ("name" , pkg .FullName ()).Debug ("marked package as executed" )
330
+ return nil
331
+ }
332
+
333
+ func (c filesystemExecCache ) filename (pkg * leeway.Package ) (string , error ) {
334
+ v , err := pkg .Version ()
335
+ if err != nil {
336
+ return "" , err
337
+ }
338
+ return filepath .Join (string (c ), fmt .Sprintf ("%s.executed" , v )), nil
339
+ }
340
+
341
+ func (c filesystemExecCache ) NeedsExecution (ctx context.Context , pkg * leeway.Package ) (bool , error ) {
342
+ fn , err := c .filename (pkg )
343
+ if err != nil {
344
+ return false , err
345
+ }
346
+ _ , err = os .Stat (fn )
347
+ if os .IsNotExist (err ) {
348
+ return true , nil
349
+ }
350
+ return false , err
351
+ }
0 commit comments