Skip to content

Commit

Permalink
feat: better support for workflow versions (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
abelanger5 authored Jan 2, 2024
1 parent 2e87128 commit bf16ba3
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 16 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ go 1.21
replace golang.org/x/exp => golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1

require (
github.com/Masterminds/semver/v3 v3.2.1
github.com/creasty/defaults v1.7.0
github.com/fatih/color v1.16.0
github.com/getkin/kin-openapi v0.122.0
Expand Down
11 changes: 7 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
Expand Down Expand Up @@ -79,6 +81,8 @@ github.com/go-chi/chi v1.5.5 h1:vOB/HbEMt9QqBqErz07QehcOKHaWFtuj87tTDVz2qXE=
github.com/go-chi/chi v1.5.5/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw=
github.com/go-co-op/gocron v1.36.0 h1:sEmAwg57l4JWQgzaVWYfKZ+w13uHOqeOtwjo72Ll5Wc=
github.com/go-co-op/gocron v1.36.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
Expand Down Expand Up @@ -245,7 +249,10 @@ github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down Expand Up @@ -282,12 +289,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/steebchen/prisma-client-go v0.31.3 h1:ubqSRFfPUTAHZijY/HqeTlKJWJpI3Lj4O338AqvqBeg=
github.com/steebchen/prisma-client-go v0.31.3/go.mod h1:ksKELgUZSn56rbAv1jlF8D7o8V6lis0Tc2LEgv2qNbs=
github.com/steebchen/prisma-client-go v0.31.4-0.20231228102837-d2b2373128a2 h1:8vofR6qaIWoTi2AGymk1MlipkUWLjBNjKjYA8DYaHps=
github.com/steebchen/prisma-client-go v0.31.4-0.20231228102837-d2b2373128a2/go.mod h1:ksKELgUZSn56rbAv1jlF8D7o8V6lis0Tc2LEgv2qNbs=
github.com/steebchen/prisma-client-go v0.31.4-0.20231228103622-90ae64ae6cc1 h1:cd+ognB0V1nb/IACjl+PA3f3TFnkGLXQY2J2EpDPuNk=
github.com/steebchen/prisma-client-go v0.31.4-0.20231228103622-90ae64ae6cc1/go.mod h1:shY2GTQyv15WYTE4p2zffr01ratTzX0zXtBWnDHiLpo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
Expand Down
2 changes: 1 addition & 1 deletion internal/repository/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type CreateWorkflowVersionOpts struct {
Description *string

// (required) the workflow version
Version string `validate:"required"`
Version string `validate:"required,semver"`

// (optional) event triggers for the workflow
EventTriggers []string
Expand Down
7 changes: 7 additions & 0 deletions internal/validator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"regexp"
"unicode"

"github.com/Masterminds/semver/v3"
"github.com/go-playground/validator/v10"
"github.com/google/uuid"
"github.com/hatchet-dev/hatchet/pkg/client/types"
Expand Down Expand Up @@ -42,6 +43,12 @@ func newValidator() *validator.Validate {
return action.IntegrationID != "" && action.Verb != ""
})

validate.RegisterValidation("semver", func(fl validator.FieldLevel) bool {
_, err := semver.NewVersion(fl.Field().String())

return err == nil
})

return validate
}

Expand Down
65 changes: 58 additions & 7 deletions pkg/client/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"

"github.com/Masterminds/semver/v3"
admincontracts "github.com/hatchet-dev/hatchet/internal/services/admin/contracts"
"github.com/hatchet-dev/hatchet/internal/validator"
"github.com/hatchet-dev/hatchet/pkg/client/types"
Expand All @@ -15,7 +16,7 @@ import (
)

type AdminClient interface {
PutWorkflow(workflow *types.Workflow) error
PutWorkflow(workflow *types.Workflow, opts ...PutOptFunc) error
}

type adminClientImpl struct {
Expand All @@ -37,19 +38,45 @@ func newAdmin(conn *grpc.ClientConn, opts *sharedClientOpts) AdminClient {
}
}

func (a *adminClientImpl) PutWorkflow(workflow *types.Workflow) error {
opts, err := a.getPutOpts(workflow)
type putOpts struct {
autoVersion bool
}

type PutOptFunc func(*putOpts)

func WithAutoVersion() PutOptFunc {
return func(opts *putOpts) {
opts.autoVersion = true
}
}

func defaultPutOpts() *putOpts {
return &putOpts{}
}

func (a *adminClientImpl) PutWorkflow(workflow *types.Workflow, fs ...PutOptFunc) error {
opts := defaultPutOpts()

for _, f := range fs {
f(opts)
}

if workflow.Version == "" && !opts.autoVersion {
return fmt.Errorf("PutWorkflow error: workflow version is required, or use WithAutoVersion()")
}

req, err := a.getPutRequest(workflow)

if err != nil {
return fmt.Errorf("could not get put opts: %w", err)
}

apiWorkflow, err := a.client.GetWorkflowByName(context.Background(), &admincontracts.GetWorkflowByNameRequest{
TenantId: a.tenantId,
Name: opts.Opts.Name,
Name: req.Opts.Name,
})

shouldPut := false
shouldPut := opts.autoVersion

if err != nil {
// if not found, create
Expand All @@ -58,6 +85,10 @@ func (a *adminClientImpl) PutWorkflow(workflow *types.Workflow) error {
} else {
return fmt.Errorf("could not get workflow: %w", err)
}

if workflow.Version == "" && opts.autoVersion {
req.Opts.Version = "0.1.0"
}
} else {
// if there are no versions, exit
if len(apiWorkflow.Versions) == 0 {
Expand All @@ -68,10 +99,18 @@ func (a *adminClientImpl) PutWorkflow(workflow *types.Workflow) error {
if apiWorkflow.Versions[0].Version != workflow.Version {
shouldPut = true
}

if workflow.Version == "" && opts.autoVersion {
req.Opts.Version, err = bumpMinorVersion(apiWorkflow.Versions[0].Version)

if err != nil {
return fmt.Errorf("could not bump version: %w", err)
}
}
}

if shouldPut {
_, err = a.client.PutWorkflow(context.Background(), opts)
_, err = a.client.PutWorkflow(context.Background(), req)

if err != nil {
return fmt.Errorf("could not create workflow: %w", err)
Expand All @@ -81,7 +120,7 @@ func (a *adminClientImpl) PutWorkflow(workflow *types.Workflow) error {
return nil
}

func (a *adminClientImpl) getPutOpts(workflow *types.Workflow) (*admincontracts.PutWorkflowRequest, error) {
func (a *adminClientImpl) getPutRequest(workflow *types.Workflow) (*admincontracts.PutWorkflowRequest, error) {
opts := &admincontracts.CreateWorkflowVersionOpts{
Name: workflow.Name,
Version: workflow.Version,
Expand Down Expand Up @@ -130,3 +169,15 @@ func (a *adminClientImpl) getPutOpts(workflow *types.Workflow) (*admincontracts.
Opts: opts,
}, nil
}

func bumpMinorVersion(version string) (string, error) {
currVersion, err := semver.NewVersion(version)

if err != nil {
return "", fmt.Errorf("could not parse version: %w", err)
}

newVersion := currVersion.IncMinor()

return newVersion.String(), nil
}
3 changes: 2 additions & 1 deletion pkg/worker/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"reflect"

"github.com/hatchet-dev/hatchet/pkg/client"
"github.com/hatchet-dev/hatchet/pkg/client/types"
)

Expand All @@ -23,7 +24,7 @@ func (s *Service) On(t triggerConverter, workflow workflowConverter) error {
apiWorkflow.Triggers = *wt

// create the workflow via the API
err := s.worker.client.Admin().PutWorkflow(&apiWorkflow)
err := s.worker.client.Admin().PutWorkflow(&apiWorkflow, client.WithAutoVersion())

if err != nil {
return err
Expand Down
18 changes: 15 additions & 3 deletions pkg/worker/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ func (w workflowFn) ToWorkflow(svcName string) types.Workflow {
return workflowJob.ToWorkflow(svcName)
}

func (w workflowFn) ToActionMap(svcName string) map[string]any {
return map[string]any{
getFnName(w.Function): w.Function,
}
}

type WorkflowJob struct {
// The name of the job
Name string
Expand All @@ -109,9 +115,8 @@ func (j *WorkflowJob) ToWorkflow(svcName string) types.Workflow {
}

return types.Workflow{
Name: j.Name,
Version: "v0.1.0",
Jobs: jobs,
Name: j.Name,
Jobs: jobs,
}
}

Expand Down Expand Up @@ -157,6 +162,9 @@ type WorkflowStep struct {

// The executed function
Function any

// The step id. If not set, one will be generated from the function name
ID string
}

type step struct {
Expand Down Expand Up @@ -221,6 +229,10 @@ func (s *WorkflowStep) ToWorkflowStep(prevStep *step, svcName string, index int)
}

func (s *WorkflowStep) GetStepId(index int) string {
if s.ID != "" {
return s.ID
}

stepId := getFnName(s.Function)

// this can happen if the function is anonymous
Expand Down

0 comments on commit bf16ba3

Please sign in to comment.