Skip to content

Commit

Permalink
🤟 trace: wire
Browse files Browse the repository at this point in the history
  • Loading branch information
rjeczalik committed Jun 30, 2024
1 parent 8907865 commit afe96b1
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 48 deletions.
3 changes: 3 additions & 0 deletions pkg/hookt/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"hookt.dev/cmd/pkg/errors"
"hookt.dev/cmd/pkg/plugin/builtin"
"hookt.dev/cmd/pkg/proto"
"hookt.dev/cmd/pkg/trace"
)

var plugins []proto.Interface
Expand Down Expand Up @@ -53,7 +54,9 @@ func (e *Engine) Run(ctx context.Context, file string) (*check.S, error) {
var g errgroup.Group

for _, job := range w.Jobs {
ctx := trace.With(ctx, "job", job.ID)
for _, step := range job.Steps {
ctx := trace.With(ctx, "step", step.ID)
g.Go(func() error {
r, ok := step.With.(proto.Runner)
if !ok {
Expand Down
35 changes: 35 additions & 0 deletions pkg/id/id.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package id

import (
"math/rand"
"strings"
)

const (
all = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
)

func Gen(n int) string {
return gen(all, 6, 1<<6-1, 63/6, n)
}

// source: https://stackoverflow.com/questions/22892120/
func gen(chars string, idxBits uint, idxMask int64, idxMax int, n int) string {
var sb strings.Builder

sb.Grow(n)

for i, cache, remain := n-1, rand.Int63(), idxMax; i >= 0; {
if remain == 0 {
cache, remain = rand.Int63(), idxMax
}
if idx := int(cache & idxMask); idx < len(chars) {
sb.WriteByte(chars[idx])
i--
}
cache >>= idxBits
remain--
}

return sb.String()
}
25 changes: 9 additions & 16 deletions pkg/plugin/builtin/event/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package event // import "hookt.dev/cmd/pkg/plugin/builtin/event"

import (
"context"
"encoding/json"
"log/slog"
"time"

Expand Down Expand Up @@ -94,7 +93,7 @@ func (p *Plugin) process() {
}
}

func (p *Plugin) Step(context.Context) any {
func (p *Plugin) Step(ctx context.Context) any {
c := make(chan proto.Message)
p.c[c] = struct{}{}
it, _ := time.ParseDuration(p.Config.InactiveTimeout)
Expand All @@ -113,32 +112,26 @@ type Step struct {
it time.Duration
}

func str(v any) string {
if v == nil {
return ""
}
p, _ := json.Marshal(v)
return string(p)
}

func (s *Step) Run(ctx context.Context, c *check.S) error {
slog.Debug("event: run",
"match", str(s.Match),
"pass", str(s.Pass),
"fail", str(s.Fail),
"match", s.Match,
"pass", s.Pass,
"fail", s.Fail,
)

match, err := s.p.p.Pattern(ctx, s.Match)
// tr := trace.ContextStep(ctx)

match, err := s.p.p.Patterns(ctx, s.Match)
if err != nil {
return errors.New("failed to parse match pattern: %w", err)
}

pass, err := s.p.p.Pattern(ctx, s.Pass)
pass, err := s.p.p.Patterns(ctx, s.Pass)
if err != nil {
return errors.New("failed to parse pass pattern: %w", err)
}

fail, err := s.p.p.Pattern(ctx, s.Fail)
fail, err := s.p.p.Patterns(ctx, s.Fail)
if err != nil {
return errors.New("failed to parse fail pattern: %w", err)
}
Expand Down
45 changes: 31 additions & 14 deletions pkg/proto/pattern.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"log/slog"
"sort"
"text/template"

"hookt.dev/cmd/pkg/errors"
Expand All @@ -15,14 +16,19 @@ import (
"sigs.k8s.io/yaml"
)

type Pattern map[*gojq.Query]func(context.Context, any) (bool, error)
type Pattern struct {
Key *gojq.Query
Match func(context.Context, any) (bool, error)
}

type Patterns []*Pattern

func (p Pattern) Match(ctx context.Context, obj any) (bool, error) {
for q, fn := range p {
it := q.RunWithContext(ctx, obj)
func (p Patterns) Match(ctx context.Context, obj any) (bool, error) {
for _, p := range p {
it := p.Key.RunWithContext(ctx, obj)

slog.Debug("pattern",
"query", q.String(),
"query", p.Key.String(),
)

// TODO: Handle multiple results?
Expand All @@ -31,9 +37,9 @@ func (p Pattern) Match(ctx context.Context, obj any) (bool, error) {
return false, nil
}

ok, err := fn(ctx, v)
ok, err := p.Match(ctx, v)
if err != nil {
return false, errors.New("failed to match jq %q: %w", q.String(), err)
return false, errors.New("failed to match jq %q: %w", p.Key.String(), err)
}
if !ok {
return false, nil
Expand All @@ -43,16 +49,21 @@ func (p Pattern) Match(ctx context.Context, obj any) (bool, error) {
return len(p) != 0, nil
}

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

for k, raw := range obj {
q, e := gojq.Parse(k)
tr.ParseKey(k, q, e)
var (
e error
q Pattern
)

q.Key, e = gojq.Parse(k)
tr.ParseKey(k, q.Key, e)
if e != nil {
err = errors.Join(
err,
Expand Down Expand Up @@ -80,20 +91,26 @@ func (p *P) Pattern(ctx context.Context, obj wire.Object) (Pattern, error) {

switch want := want.(type) {
case bool:
pt[q] = func(_ context.Context, got any) (bool, error) { return want || got == nil, nil }
q.Match = func(_ context.Context, got any) (bool, error) { return want || got == nil, nil }
case string:
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)
q.Match = p.t.Match(trace.WithPattern(ctx, tr), want)
default:
pt[q] = func(_ context.Context, got any) (bool, error) {
q.Match = func(_ context.Context, got any) (bool, error) {
ok := cmpEqual(want, got)
tr.EqualMatch(k, want, got, ok)
return ok, nil
}
}

pt = append(pt, &q)
}

sort.Slice(pt, func(i, j int) bool {
return pt[i].Key.String() < pt[j].Key.String()
})

return pt, err
}

Expand Down
58 changes: 49 additions & 9 deletions pkg/proto/proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"encoding/json"
"log/slog"
"strconv"
"strings"

"hookt.dev/cmd/pkg/errors"
"hookt.dev/cmd/pkg/proto/wire"
Expand All @@ -17,6 +19,7 @@ type Workflow struct {
}

type Job struct {
ID string
Plugins []Plugin
Steps []Step
}
Expand Down Expand Up @@ -66,18 +69,33 @@ func (p *P) Parse(ctx context.Context, q []byte) (*Workflow, error) {

w.Jobs = make([]Job, len(raw.Jobs))

uniq := make(map[string]struct{})

for i, job := range raw.Jobs {
j := &w.Jobs[i]

if strings.HasPrefix(job.ID, "#") {
return nil, errors.New("#job-%d: error reading job: id cannot start with #", i)
}

slog.Debug("wiring jobs",
"index", i,
"job", job.ID,
)

tr.WireJob(i, &job)

j.ID = nonempty(job.ID, "#job-"+strconv.Itoa(i))
j.Plugins = make([]Plugin, len(job.Plugins))

if _, ok := uniq[j.ID]; ok {
return nil, errors.New("#job-%d: error reading job: duplicate id %q", i, j.ID)
}

uniq[j.ID] = struct{}{}

ctx := trace.With(ctx, "job", j.ID)

for k, plugin := range job.Plugins {
iface, ok := p.m[plugin.Uses]
if !ok {
Expand All @@ -104,29 +122,41 @@ func (p *P) Parse(ctx context.Context, q []byte) (*Workflow, error) {

j.Steps = make([]Step, len(job.Steps))

uniq := make(map[string]struct{})

for k, step := range job.Steps {
iface, ok := p.m[step.Uses]
if !ok {
return nil, errors.New("error reading plugin %q step: not found", step.Uses)
return nil, errors.New("#step-%d: error reading plugin %q step: not found", k, step.Uses)
}

slog.Debug("wiring steps",
"index", k,
"step", step.Uses,
"with", string(step.With),
)
if strings.HasPrefix(step.ID, "#") {
return nil, errors.New("#step-%d: error reading plugin %q step: id cannot start with #", k, step.Uses)
}

s := &j.Steps[k]

s.Uses = step.Uses
s.ID = step.ID
s.ID = nonempty(step.ID, "#step-"+strconv.Itoa(k))
s.Desc = step.Desc
s.With = iface.Step(ctx)
s.With = iface.Step(trace.With(ctx, "step", s.ID))

if _, ok := uniq[s.ID]; ok {
return nil, errors.New("error reading plugin %q step: duplicate id %q", step.Uses, s.ID)
}

uniq[s.ID] = struct{}{}

if err := yaml.Unmarshal(step.With, s.With); err != nil {
return nil, errors.New("error reading plugin %q step: %w", step.Uses, err)
return nil, errors.New("%s: error reading plugin %q step: %w", s.ID, step.Uses, err)
}

slog.Debug("wiring steps",
"id", s.ID,
"step", step.Uses,
"with", string(step.With),
)

tr.WireStep(k, &step, s.With)
}

Expand All @@ -151,3 +181,13 @@ func (p *P) Parse(ctx context.Context, q []byte) (*Workflow, error) {

return &w, nil
}

func nonempty[T comparable](t ...T) T {
var zero T
for _, v := range t {
if v != zero {
return v
}
}
return zero
}
17 changes: 9 additions & 8 deletions pkg/proto/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,28 +64,29 @@ func (t *T) funcs() template.FuncMap {
}

func (t *T) Match(ctx context.Context, data string) func(context.Context, any) (bool, error) {
const inherit = "" // the key is going to be overriden

tr := trace.ContextPattern(ctx)
tmpl, err := t.Parse("", data)
tr.TemplateValue("", data, tmpl, err)
tr.TemplateValue(inherit, data, tmpl, err)
if err != nil {
return func(context.Context, any) (bool, error) { return false, err }
}
return func(ctx context.Context, got any) (bool, error) {
var (
buf bytes.Buffer
tr = trace.ContextPattern(ctx)
)
var buf bytes.Buffer
// TODO: Fix
// tr = trace.ContextPattern(ctx)

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

var want any

err = yaml.Unmarshal(buf.Bytes(), &want)
tr.UnmarshalMatch("", buf.Bytes(), want, err)
tr.UnmarshalMatch(inherit, buf.Bytes(), want, err)
if err != nil {
return false, errors.New("failed to parse result: %w", err)
}
Expand All @@ -95,7 +96,7 @@ func (t *T) Match(ctx context.Context, data string) func(context.Context, any) (
return want, nil
default:
ok := cmpEqual(want, got)
tr.EqualMatch("", want, got, ok)
tr.EqualMatch(inherit, want, got, ok)
return ok, nil
}
}
Expand Down
Loading

0 comments on commit afe96b1

Please sign in to comment.