M cmd/amitm/main.go => cmd/amitm/main.go +9 -2
@@ 1,11 1,11 @@
package main
import (
- "fmt"
"log"
"9fans.net/go/acme"
"git.sr.ht/~nvkv/amitm/internal/config/v1"
+ "git.sr.ht/~nvkv/amitm/internal/executor/v1"
)
func main() {
@@ 26,8 26,15 @@ func main() {
}
rules, ok := config.RulesForAction(event.Op)
+ matched := executor.Match(rules, event)
if ok {
- fmt.Printf("%+v -> %+v", event, rules)
+ for _, rule := range matched {
+ out, err := executor.Apply(rule, event.Op, event.Name)
+ log.Printf("%s: %s\n", rule.Name, string(out))
+ if err != nil {
+ log.Printf("error: %s\n", err)
+ }
+ }
}
}
}
M examples/amitm.toml => examples/amitm.toml +11 -3
@@ 1,10 1,18 @@
[[rules]]
name = "Golang"
-glob = "*.go"
+globs = ["*.go"]
action = "put"
[[rules.pipeline]]
-exec = "go fmt $file"
+exec = ["go", "fmt", "$file"]
[[rules.pipeline]]
-exec = "echo 'Done formatting $file'">
\ No newline at end of file
+exec = ["echo", "'Done formatting $file'"]
+
+[[rules]]
+name = "Terraform"
+globs = ["*.tf", "*.tfvars"]
+action = "put"
+
+[[rules.pipeline]]
+exec = ["terraform13", "fmt", "$file"]<
\ No newline at end of file
M internal/config/v1/config.go => internal/config/v1/config.go +5 -5
@@ 6,13 6,13 @@ import (
)
type PipelineStep struct {
- Exec string
+ Exec []string
}
type Rule struct {
Name string
Action string
- Glob string
+ Globs []string
Pipeline []PipelineStep
}
@@ 41,8 41,8 @@ func NewConfig(data []byte) (*Config, error) {
return nil, err
}
cfg.actionmap = make(map[string][]*Rule)
- for _, rule := range cfg.Rules {
- cfg.actionmap[rule.Action] = append(cfg.actionmap[rule.Action], &rule)
+ for i, rule := range cfg.Rules {
+ cfg.actionmap[rule.Action] = append(cfg.actionmap[rule.Action], &cfg.Rules[i])
}
return cfg, nil
-}>
\ No newline at end of file
+}
M internal/config/v1/config_test.go => internal/config/v1/config_test.go +20 -4
@@ 8,15 8,31 @@ import (
func TestReadConfig(t *testing.T) {
fixture := []byte(`
[[rules]]
-name = "Testing tests"
-glob = "test*.test"
+name = "Golang"
+globs = ["*.go"]
action = "put"
[[rules.pipeline]]
-exec = "test -t $file"`)
+exec = ["go", "fmt", "$file"]
+
+[[rules.pipeline]]
+exec = ["echo", "'Done formatting $file'"]
+
+[[rules]]
+name = "Terraform"
+globs = ["*.tf", "*.tfvars"]
+action = "put"
+
+[[rules.pipeline]]
+exec = ["terraform13", "fmt", "$file"]`)
cfg, err := NewConfig(fixture)
- fmt.Printf("cfg: %+v\n", cfg.actionmap)
+ for k, rs := range cfg.actionmap {
+ fmt.Printf("%s =>\n", k)
+ for _, r := range rs {
+ fmt.Printf("\t%s\n", r.Name)
+ }
+ }
if err != nil || cfg == nil {
t.Errorf("Can't read %s file: %s", fixture, err)
}
A internal/executor/v1/executor.go => internal/executor/v1/executor.go +64 -0
@@ 0,0 1,64 @@
+package executor
+
+import (
+ "9fans.net/go/acme"
+ "git.sr.ht/~nvkv/amitm/internal/config/v1"
+
+ "fmt"
+ "os/exec"
+ "path/filepath"
+ "strings"
+)
+
+func Match(rules []*config.Rule, event acme.LogEvent) []*config.Rule {
+ var toApply []*config.Rule
+
+ for _, rule := range rules {
+ var applicable = false
+ for _, glob := range rule.Globs {
+ ok, _ := filepath.Match(glob, filepath.Base(event.Name))
+ if ok {
+ applicable = true
+ break
+ }
+ }
+ if applicable {
+ toApply = append(toApply, rule)
+ }
+ }
+ return toApply
+}
+
+func Apply(rule *config.Rule, op, file string) ([]byte, error) {
+ if op != rule.Action {
+ return nil, fmt.Errorf(
+ "action mismatch. Can't apply rule for %s to operation %s on file %s",
+ rule.Action,
+ op,
+ file,
+ )
+ }
+
+ var output []byte
+
+ for _, step := range rule.Pipeline {
+ if len(step.Exec) > 0 {
+ prog := step.Exec[0]
+ origArgs := step.Exec[1:]
+ args := make([]string, len(origArgs))
+ copy(args, origArgs)
+
+ for i, arg := range args {
+ args[i] = strings.Replace(arg, "$file", file, -1)
+ }
+
+ cmd := exec.Command(prog, args...)
+ out, err := cmd.CombinedOutput()
+ output = append(output, out...)
+ if err != nil {
+ return output, err
+ }
+ }
+ }
+ return output, nil
+}