~nvkv/amitm

bffe321917e01d7fb2f6516c6a4bafb506c339d5 — Semyon Novikov a month ago 2ababae
Make amitm minimally useful
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
}