Skip to content

Commit

Permalink
😱 trace: patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
rjeczalik committed Jun 29, 2024
1 parent cfd58e4 commit 8907865
Show file tree
Hide file tree
Showing 13 changed files with 338 additions and 49 deletions.
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.PHONY: all

all:
go build -v ./...
go test -race -v ./...
1 change: 1 addition & 0 deletions cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package cmd // import "hookt.dev/cmd"
28 changes: 28 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module hookt.dev/cmd

go 1.22.3

require (
github.com/Masterminds/sprig v2.22.0+incompatible
github.com/google/go-cmp v0.6.0
github.com/itchyny/gojq v0.12.16
github.com/lmittmann/tint v1.0.4
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
golang.org/x/sync v0.7.0
sigs.k8s.io/yaml v1.4.0
)

require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/itchyny/timefmt-go v0.1.6 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/stretchr/testify v1.9.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
)
49 changes: 49 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/itchyny/gojq v0.12.16 h1:yLfgLxhIr/6sJNVmYfQjTIv0jGctu6/DgDoivmxTr7g=
github.com/itchyny/gojq v0.12.16/go.mod h1:6abHbdC2uB9ogMS38XsErnfqJ94UlngIJGlRAIj4jTM=
github.com/itchyny/timefmt-go v0.1.6 h1:ia3s54iciXDdzWzwaVKXZPbiXzxxnv1SPGFfM/myJ5Q=
github.com/itchyny/timefmt-go v0.1.6/go.mod h1:RRDZYC5s9ErkjQvTvvU7keJjxUYzIISJGxm9/mAERQg=
github.com/lmittmann/tint v1.0.4 h1:LeYihpJ9hyGvE0w+K2okPTGUdVLfng1+nDNVR4vWISc=
github.com/lmittmann/tint v1.0.4/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
8 changes: 7 additions & 1 deletion hkt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os/signal"

"hookt.dev/cmd/pkg/command"
"hookt.dev/cmd/pkg/trace"

"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -60,7 +61,12 @@ func newRunCommand(ctx context.Context, app *command.App) *cobra.Command {
cmd := &cobra.Command{
Use: "run",
Args: cobra.ExactArgs(1),
RunE: func(_ *cobra.Command, files []string) error {
RunE: func(cmd *cobra.Command, files []string) error {
if cmd.Flags().Changed("debug") {
ctx = trace.WithJob(ctx, trace.LogJob())
ctx = trace.WithPattern(ctx, trace.LogPattern())
}

s, err := app.Engine.Run(ctx, files[0])
if err != nil {
return err
Expand Down
6 changes: 3 additions & 3 deletions pkg/hookt/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ package hookt

import (
"context"
"io/ioutil"
"log/slog"
"os"

"github.com/lmittmann/tint"
"golang.org/x/sync/errgroup"
"hookt.dev/cmd/pkg/check"
"hookt.dev/cmd/pkg/errors"
"hookt.dev/cmd/pkg/plugin/builtin"
"hookt.dev/cmd/pkg/proto"
"golang.org/x/sync/errgroup"
)

var plugins []proto.Interface
Expand Down Expand Up @@ -38,7 +38,7 @@ func New(opts ...func(*Engine)) *Engine {
}

func (e *Engine) Run(ctx context.Context, file string) (*check.S, error) {
p, err := ioutil.ReadFile(file)
p, err := os.ReadFile(file)
if err != nil {
return nil, errors.New("failed to read file: %w", err)
}
Expand Down
57 changes: 44 additions & 13 deletions pkg/proto/pattern.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package proto

import (
"context"
"fmt"
"log/slog"
"text/template"

"hookt.dev/cmd/pkg/errors"
"hookt.dev/cmd/pkg/proto/wire"
"hookt.dev/cmd/pkg/trace"

"github.com/google/go-cmp/cmp"
"github.com/itchyny/gojq"
Expand Down Expand Up @@ -41,34 +44,62 @@ func (p Pattern) Match(ctx context.Context, obj any) (bool, error) {
}

func (p *P) Pattern(ctx context.Context, obj wire.Object) (Pattern, error) {
pt := make(Pattern)
var (
pt = make(Pattern)
tr = trace.ContextPattern(ctx)
err error
)

for k, raw := range obj {
q, err := gojq.Parse(k)
if err != nil {
return nil, errors.New("failed to parse jq %q: %w", k, err)
q, e := gojq.Parse(k)
tr.ParseKey(k, q, e)
if e != nil {
err = errors.Join(
err,
errors.New("failed to parse jq %q: %w", k, e),
)
continue
}

var v any
var want any

if err := yaml.Unmarshal(raw, &v); err != nil {
return nil, errors.New("failed to parse value for jq %q: %w", k, err)
e = yaml.Unmarshal(raw, &want)
tr.UnmarshalValue(k, raw, want, e)
if e != nil {
err = errors.Join(
err,
errors.New("failed to parse value for jq %q: %w", k, e),
)
continue
}

slog.Debug("building pattern",
"key", k,
"pattern", v,
"pattern", want,
)

switch v := v.(type) {
switch want := want.(type) {
case bool:
pt[q] = func(_ context.Context, x any) (bool, error) { return v || x == nil, nil }
pt[q] = func(_ context.Context, got any) (bool, error) { return want || got == nil, nil }
case string:
pt[q] = p.t.Match(ctx, v)
tv := tr.TemplateValue
tr.TemplateValue = func(_, v string, t *template.Template, e error) { tv(k, v, t, e) }
pt[q] = p.t.Match(trace.WithPattern(ctx, tr), want)
default:
pt[q] = func(_ context.Context, x any) (bool, error) { return cmp.Equal(x, v), nil }
pt[q] = func(_ context.Context, got any) (bool, error) {
ok := cmpEqual(want, got)
tr.EqualMatch(k, want, got, ok)
return ok, nil
}
}
}

return pt, nil
return pt, err
}

func cmpEqual(want, got any) bool {
if fmt.Sprint(want) == fmt.Sprint(got) {
return true
}
return cmp.Equal(want, got)
}
9 changes: 6 additions & 3 deletions pkg/proto/pattern_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package proto_test

import (
"context"
"strconv"
"testing"

"hookt.dev/cmd/pkg/proto/wire"
"hookt.dev/cmd/pkg/trace"
)

func TestPattern(t *testing.T) {
Expand Down Expand Up @@ -74,7 +76,7 @@ func TestPattern(t *testing.T) {
},
5: {
wire.Object{
".foo.bar": []byte(`"${{ setvar "bar" . }}"`),
".foo.bar": []byte(strconv.Quote(`${{ setvar "bar" . }}`)),
},
map[string]any{
"foo": map[string]any{
Expand All @@ -85,7 +87,7 @@ func TestPattern(t *testing.T) {
},
6: {
wire.Object{
".foo.bar": []byte(`"${{ var "bar" }}"`),
".foo.bar": []byte(strconv.Quote(`${{ var "bar" }}`)),
},
map[string]any{
"foo": map[string]any{
Expand Down Expand Up @@ -136,6 +138,7 @@ func TestPattern(t *testing.T) {

p := newP()
ctx := context.Background()
ctx = trace.WithPattern(ctx, trace.LogPattern())

for _, cas := range cases {
t.Run("", func(t *testing.T) {
Expand All @@ -146,7 +149,7 @@ func TestPattern(t *testing.T) {

ok, err := pt.Match(ctx, cas.obj)
if err != nil {
t.Fatal(err)
t.Fatalf("match: Match()=%+v", err)
}

if ok != cas.ok {
Expand Down
2 changes: 1 addition & 1 deletion pkg/proto/proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (p *P) Parse(ctx context.Context, q []byte) (*Workflow, error) {

var (
w Workflow
tr = trace.ContextJobTrace(ctx)
tr = trace.ContextJob(ctx)
)

w.Jobs = make([]Job, len(raw.Jobs))
Expand Down
24 changes: 20 additions & 4 deletions pkg/proto/proto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,29 @@ package proto_test

import (
"context"
"io/ioutil"
"log/slog"
"os"
"testing"
"time"

"hookt.dev/cmd/pkg/plugin/builtin"
"hookt.dev/cmd/pkg/proto"

"github.com/davecgh/go-spew/spew"
"github.com/lmittmann/tint"
)

func init() {
slog.SetDefault(
slog.New(
tint.NewHandler(os.Stderr, &tint.Options{
AddSource: true,
Level: slog.LevelDebug,
TimeFormat: time.Kitchen,
}),
),
)
}

func newP() *proto.P {
p := builtin.Plugins()
q := make([]proto.Interface, len(p))
Expand All @@ -32,13 +46,15 @@ func TestParse(t *testing.T) {
t.Fatal(err)
}

spew.Dump(w)
_ = w

// spew.Dump(w)
}

func file(t *testing.T, path string) []byte {
t.Helper()

p, err := ioutil.ReadFile(path)
p, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
Expand Down
31 changes: 21 additions & 10 deletions pkg/proto/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import (

"hookt.dev/cmd/pkg/async"
"hookt.dev/cmd/pkg/errors"
"hookt.dev/cmd/pkg/trace"

"github.com/Masterminds/sprig"
"github.com/google/go-cmp/cmp"
"sigs.k8s.io/yaml"
)

Expand Down Expand Up @@ -64,28 +64,39 @@ func (t *T) funcs() template.FuncMap {
}

func (t *T) Match(ctx context.Context, data string) func(context.Context, any) (bool, error) {
tr := trace.ContextPattern(ctx)
tmpl, err := t.Parse("", data)
tr.TemplateValue("", data, tmpl, err)
if err != nil {
return func(context.Context, any) (bool, error) { return false, err }
}
return func(_ context.Context, x any) (bool, error) {
var buf bytes.Buffer

if err := tmpl.Execute(&buf, x); err != nil {
return func(ctx context.Context, got any) (bool, error) {
var (
buf bytes.Buffer
tr = trace.ContextPattern(ctx)
)

err := tmpl.Execute(&buf, got)
tr.ExecuteMatch("", buf.Bytes(), err)
if err != nil {
return false, errors.New("failed to evaluate %q: %w", data, err)
}

var v any
var want any

if err := yaml.Unmarshal(buf.Bytes(), &v); err != nil {
err = yaml.Unmarshal(buf.Bytes(), &want)
tr.UnmarshalMatch("", buf.Bytes(), want, err)
if err != nil {
return false, errors.New("failed to parse result: %w", err)
}

switch v := v.(type) {
switch want := want.(type) {
case bool:
return v, nil
return want, nil
default:
return cmp.Equal(x, v), nil
ok := cmpEqual(want, got)
tr.EqualMatch("", want, got, ok)
return ok, nil
}
}
}
Expand Down
Loading

0 comments on commit 8907865

Please sign in to comment.