github.com/speakeasy-api/sdk-gen-config@v1.14.2/workflow/workflow.go (about) 1 package workflow 2 3 import ( 4 "errors" 5 "fmt" 6 "io/fs" 7 "os" 8 "path/filepath" 9 "strings" 10 11 "github.com/speakeasy-api/sdk-gen-config/workspace" 12 "gopkg.in/yaml.v3" 13 ) 14 15 const ( 16 WorkflowVersion = "1.0.0" 17 workflowFile = "workflow.yaml" 18 ) 19 20 // Ensure your update schema/workflow.schema.json on changes 21 type Workflow struct { 22 Version string `yaml:"workflowVersion"` 23 SpeakeasyVersion Version `yaml:"speakeasyVersion,omitempty"` 24 Sources map[string]Source `yaml:"sources"` 25 Targets map[string]Target `yaml:"targets"` 26 } 27 28 type Version string 29 30 func Load(dir string) (*Workflow, string, error) { 31 res, err := workspace.FindWorkspace(dir, workspace.FindWorkspaceOptions{ 32 FindFile: workflowFile, 33 Recursive: true, 34 }) 35 if err != nil { 36 if !errors.Is(err, fs.ErrNotExist) { 37 return nil, "", err 38 } 39 return nil, "", fmt.Errorf("%w in %s", err, filepath.Join(dir, workspace.SpeakeasyFolder, "workflow.yaml")) 40 } 41 42 var workflow Workflow 43 if err := yaml.Unmarshal(res.Data, &workflow); err != nil { 44 return nil, "", fmt.Errorf("failed to unmarshal workflow.yaml: %w", err) 45 } 46 47 return &workflow, res.Path, nil 48 } 49 50 // Save the workflow to the given directory, dir should generally be the root of the project, and the workflow will be saved to ${projectRoot}/.speakeasy/workflow.yaml 51 func Save(dir string, workflow *Workflow) error { 52 data, err := yaml.Marshal(workflow) 53 if err != nil { 54 return fmt.Errorf("failed to marshal workflow: %w", err) 55 } 56 57 res, err := workspace.FindWorkspace(dir, workspace.FindWorkspaceOptions{ 58 FindFile: workflowFile, 59 Recursive: true, 60 }) 61 if err != nil { 62 if !errors.Is(err, fs.ErrNotExist) { 63 return err 64 } 65 res = &workspace.FindWorkspaceResult{ 66 Path: filepath.Join(dir, workspace.SpeakeasyFolder, "workflow.yaml"), 67 } 68 } 69 70 if err := os.WriteFile(res.Path, data, 0o644); err != nil { 71 return fmt.Errorf("failed to write workflow.yaml: %w", err) 72 } 73 74 return nil 75 } 76 77 func (w Workflow) Validate(supportLangs []string) error { 78 if w.Version != WorkflowVersion { 79 return fmt.Errorf("unsupported workflow version: %s", w.Version) 80 } 81 82 if len(w.Sources) == 0 && len(w.Targets) == 0 { 83 return fmt.Errorf("no sources or targets found") 84 } 85 86 for targetID, target := range w.Targets { 87 if err := target.Validate(supportLangs, w.Sources); err != nil { 88 return fmt.Errorf("failed to validate target %s: %w", targetID, err) 89 } 90 } 91 92 for sourceID, source := range w.Sources { 93 if err := source.Validate(); err != nil { 94 return fmt.Errorf("failed to validate source %s: %w", sourceID, err) 95 } 96 } 97 98 return nil 99 } 100 101 func (w Workflow) GetTargetSource(target string) (*Source, string, error) { 102 t, ok := w.Targets[target] 103 if !ok { 104 return nil, "", fmt.Errorf("target %s not found", target) 105 } 106 107 s, ok := w.Sources[t.Source] 108 if ok { 109 return &s, "", nil 110 } else { 111 return nil, t.Source, nil 112 } 113 } 114 115 func validateSecret(secret string) error { 116 if !strings.HasPrefix(secret, "$") { 117 return fmt.Errorf("secret must be a environment variable reference (ie $MY_SECRET)") 118 } 119 120 return nil 121 } 122 123 func (v Version) String() string { 124 if v == "" || v == "latest" { 125 return "latest" 126 } 127 128 if !strings.HasPrefix(string(v), "v") { 129 return "v" + string(v) 130 } 131 132 return string(v) 133 }