-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathscript.go
166 lines (153 loc) · 4.91 KB
/
script.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
package modmake
import (
"context"
"errors"
"fmt"
"io/fs"
"log"
"os"
"strings"
)
// Script will execute each Task in order, returning the first error.
func Script(fns ...Runner) Task {
return func(ctx context.Context) error {
for i, fn := range fns {
run := ContextAware(fn)
if err := run.Run(ctx); err != nil {
return fmt.Errorf("script[%d]: %w", i, err)
}
}
return nil
}
}
// IfNotExists will skip executing the Runner if the given file exists, returning nil.
// This is similar to the default Make behavior of skipping a task if the target file already exists.
func IfNotExists(file PathString, r Runner) Task {
return func(ctx context.Context) error {
if !file.Exists() {
return r.Run(ctx)
}
return nil
}
}
// IfExists will execute the Runner if the file exists, returning nil otherwise.
func IfExists(file PathString, r Runner) Task {
return func(ctx context.Context) error {
if file.Exists() {
return r.Run(ctx)
}
return nil
}
}
// IfError will create a Runner with an error handler Runner that is only executed if the base Runner returns an error.
// If both the base Runner and the handler return an error, then the handler's error will be returned.
func IfError(canErr Runner, handler Runner) Task {
return func(ctx context.Context) error {
if err := canErr.Run(ctx); err != nil {
return handler.Run(ctx)
}
return nil
}
}
// Print will create a Runner that logs a message.
func Print(msg string, args ...any) Task {
return Plain(func() {
if !strings.HasSuffix(msg, "\n") {
msg += "\n"
}
log.Printf(msg, args...)
})
}
// Chdir will change the current working directory to newWorkdir and run the Runner in that context.
// Whether the Runner executes successfully or not, the working directory will be reset back to its original state.
func Chdir(newWorkdir PathString, runner Runner) Task {
return func(ctx context.Context) (err error) {
cwd, err := Getwd()
if err != nil {
return fmt.Errorf("failed to get current working directory: %w", err)
}
if err = newWorkdir.Chdir(); err != nil {
return fmt.Errorf("failed to change the working directory to '%s': %w", newWorkdir, err)
}
defer func() {
if _err := cwd.Chdir(); _err != nil {
_err = fmt.Errorf("failed to reset the working directory to '%s': %w", cwd, _err)
if err != nil {
err = fmt.Errorf("%w, %v", err, _err)
}
}
}()
return runner.Run(ctx)
}
}
// CopyFile creates a Runner that copies a source file to target.
// The source and target file names are expected to be relative to the build's working directory, unless they are absolute paths.
// The target file will be created or truncated as appropriate.
func CopyFile(source, target PathString) Task {
return WithoutContext(func() error {
return source.CopyTo(target)
})
}
// MoveFile creates a Runner that will move the file indicated by source to target.
// The source and target file names are expected to be relative to the build's working directory, unless they are absolute paths.
// The target file will be created or truncated as appropriate.
func MoveFile(source, target PathString) Task {
return func(ctx context.Context) error {
return CopyFile(source, target).Then(Task(func(_ context.Context) error {
return source.Remove()
})).Run(ctx)
}
}
// Mkdir makes a directory named dir, if it doesn't exist already.
// If the directory already exists, then nothing is done and err will be nil.
// Any error encountered while making the directory is returned.
func Mkdir(dir PathString, perm os.FileMode) Task {
return WithoutContext(func() error {
if dir.Exists() {
return nil
}
err := dir.Mkdir(perm)
if err != nil {
if errors.Is(err, fs.ErrExist) {
return nil
}
return err
}
return nil
})
}
// MkdirAll makes the target directory, and any directories in between.
// Any error encountered while making the directories is returned.
func MkdirAll(dir PathString, perm os.FileMode) Task {
return WithoutContext(func() error {
return dir.MkdirAll(perm)
})
}
// RemoveFile will create a Runner that removes the specified file from the filesystem.
// If the file doesn't exist, then this Runner returns nil.
// Any other error encountered while removing the file is returned.
func RemoveFile(file PathString) Task {
return WithoutContext(func() error {
if !file.Exists() {
return nil
}
if file.IsDir() {
return fmt.Errorf("file '%s' is a directory, use RemoveDir instead", file)
}
return file.Remove()
})
}
// RemoveDir will create a Runner that removes the directory specified and all of its contents from the filesystem.
// If the directory doesn't exist, then this Runner returns nil.
// Any other error encountered while removing the directory is returned.
func RemoveDir(file PathString) Task {
return WithoutContext(func() error {
if !file.Exists() {
return nil
}
if file.IsFile() {
return fmt.Errorf("file '%s' is a file, use RemoveFile instead", file)
}
return file.RemoveAll()
})
}