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  }