github.com/profzone/eden-framework@v1.0.10/internal/project/workflow.go (about)

     1  package project
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/imdario/mergo"
     6  	"sort"
     7  )
     8  
     9  var PresetWorkflows = Workflows{}
    10  
    11  func RegisterWorkFlow(name string, flow *Workflow) {
    12  	PresetWorkflows[name] = flow
    13  }
    14  
    15  const (
    16  	STAGE_TEST   = "test"
    17  	STAGE_BUILD  = "build"
    18  	STAGE_SHIP   = "ship"
    19  	STAGE_DEPLOY = "deploy"
    20  )
    21  
    22  type Workflows map[string]*Workflow
    23  
    24  func (bs Workflows) List() (list []string) {
    25  	for name := range bs {
    26  		list = append(list, name)
    27  	}
    28  	sort.Strings(list)
    29  	return
    30  }
    31  
    32  type Workflow struct {
    33  	Extends     string      `yaml:"extends,omitempty"`
    34  	BranchFlows BranchFlows `yaml:"branch_flows,inline"`
    35  }
    36  
    37  func (w Workflow) GetAvailableEnv() (envs map[string]string) {
    38  	for _, flow := range w.BranchFlows {
    39  		if flow.Env != nil && len(flow.Env) > 0 {
    40  			_ = mergo.Merge(&envs, flow.Env, mergo.WithOverride)
    41  		}
    42  	}
    43  	return
    44  }
    45  
    46  func (w Workflow) TryExtendsOrSetDefaults() *Workflow {
    47  	if w.Extends != "" {
    48  		if presetFlow, ok := PresetWorkflows[w.Extends]; ok {
    49  			w.BranchFlows = presetFlow.BranchFlows.Merge(w.BranchFlows)
    50  			w.Extends = ""
    51  		}
    52  	}
    53  
    54  	for _, branchFlow := range w.BranchFlows {
    55  		for _, job := range branchFlow.Jobs {
    56  			switch job.Stage {
    57  			case STAGE_TEST, STAGE_BUILD:
    58  				if job.Builder == "" {
    59  					job.Builder = "BUILDER_${PROJECT_PROGRAM_LANGUAGE}"
    60  				}
    61  			case STAGE_SHIP:
    62  				if job.Builder == "" {
    63  					job.Builder = "BUILDER_DOCKER"
    64  				}
    65  			case STAGE_DEPLOY:
    66  				if job.Builder == "" {
    67  					job.Builder = "BUILDER_RANCHER"
    68  				}
    69  			}
    70  		}
    71  	}
    72  
    73  	return &w
    74  }
    75  
    76  type BranchFlows map[string]BranchFlow
    77  
    78  func (branchFlows BranchFlows) Merge(nextBranches BranchFlows) BranchFlows {
    79  	finalBranchFlows := BranchFlows{}
    80  	for name, branch := range branchFlows {
    81  		if nextBranch, ok := nextBranches[name]; ok {
    82  			finalBranchFlows[name] = branch.Merge(&nextBranch)
    83  			delete(nextBranches, name)
    84  		} else {
    85  			finalBranchFlows[name] = branch
    86  		}
    87  	}
    88  	for name, branch := range nextBranches {
    89  		finalBranchFlows[name] = branch
    90  	}
    91  
    92  	for name, branch := range finalBranchFlows {
    93  		if branch.Extends != "" {
    94  			if branchFlowNow, ok := finalBranchFlows[branch.Extends]; ok {
    95  				branch.Extends = ""
    96  				finalBranchFlows[name] = branchFlowNow.Merge(&branch)
    97  			}
    98  		}
    99  	}
   100  
   101  	return finalBranchFlows
   102  }
   103  
   104  type BranchFlow struct {
   105  	Skip    bool              `yaml:"skip,omitempty"`
   106  	Extends string            `yaml:"extends,omitempty"`
   107  	Env     map[string]string `yaml:"env,omitempty"`
   108  	Jobs    Jobs              `yaml:"jobs,inline,omitempty"`
   109  }
   110  
   111  func (branchFlow *BranchFlow) UnmarshalYAML(unmarshal func(interface{}) error) error {
   112  	var v string
   113  	err := unmarshal(&v)
   114  	if err == nil && v == "skip" {
   115  		branchFlow.Skip = true
   116  		return nil
   117  	}
   118  
   119  	type BranchFlowAlias BranchFlow
   120  	b := BranchFlowAlias(*branchFlow)
   121  	if err := unmarshal(&b); err != nil {
   122  		return err
   123  	}
   124  	*branchFlow = BranchFlow(b)
   125  	return nil
   126  }
   127  
   128  func (branchFlow BranchFlow) Merge(nextBranchFlow *BranchFlow) BranchFlow {
   129  	if nextBranchFlow.Env != nil && len(nextBranchFlow.Env) > 0 {
   130  		branchFlow.Env = nextBranchFlow.Env
   131  		_ = mergo.Merge(&branchFlow.Env, nextBranchFlow.Env, mergo.WithOverride)
   132  	}
   133  	if nextBranchFlow.Extends != "" {
   134  		branchFlow.Extends = nextBranchFlow.Extends
   135  	}
   136  	if len(nextBranchFlow.Jobs) > 0 {
   137  		branchFlow.Jobs = branchFlow.Jobs.Merge(nextBranchFlow.Jobs)
   138  	}
   139  	return branchFlow
   140  }
   141  
   142  type Jobs []Job
   143  
   144  func (jobs Jobs) Merge(nextJobs Jobs) Jobs {
   145  	finalJobs := Jobs{}
   146  	for _, job := range jobs {
   147  		index, nextJob := nextJobs.Find(job.Stage)
   148  		if nextJob != nil {
   149  			finalJobs = append(finalJobs, nextJob.Merge(&job))
   150  			nextJobs, _ = nextJobs.Remove(index)
   151  		} else {
   152  			finalJobs = append(finalJobs, job)
   153  		}
   154  	}
   155  	for name, job := range nextJobs {
   156  		finalJobs[name] = job
   157  	}
   158  	return finalJobs
   159  }
   160  
   161  func (jobs Jobs) Find(stage string) (int, *Job) {
   162  	for i, j := range jobs {
   163  		if j.Stage == stage {
   164  			return i, &j
   165  		}
   166  	}
   167  	return -1, nil
   168  }
   169  
   170  func (jobs Jobs) Remove(index int) (Jobs, error) {
   171  	if index == 0 {
   172  		return jobs[1:], nil
   173  	} else if index == len(jobs)-1 {
   174  		return jobs[:len(jobs)-1], nil
   175  	} else if index > len(jobs)-1 {
   176  		return jobs, fmt.Errorf("index out of range: %d, length: %d", index, len(jobs)-1)
   177  	} else {
   178  		finalJobs := Jobs{}
   179  		finalJobs = append(jobs[:index], jobs[index+1:]...)
   180  		return finalJobs, nil
   181  	}
   182  }
   183  
   184  type Job struct {
   185  	Stage     string      `yaml:"stage,omitempty"`
   186  	Skip      bool        `yaml:"skip,omitempty"`
   187  	Builder   string      `yaml:"builder,omitempty"`
   188  	Run       Script      `yaml:"run,omitempty"`
   189  	Artifacts *CIArtifact `yaml:"artifacts,omitempty"`
   190  }
   191  
   192  func (job Job) MarshalYAML() (interface{}, error) {
   193  	if job.Skip {
   194  		return "skip", nil
   195  	}
   196  	return job, nil
   197  }
   198  
   199  func (job *Job) UnmarshalYAML(unmarshal func(interface{}) error) error {
   200  	var v string
   201  	err := unmarshal(&v)
   202  	if err == nil && v == "skip" {
   203  		job.Skip = true
   204  		return nil
   205  	}
   206  	type JobAlias Job
   207  	b := JobAlias(*job)
   208  	if err := unmarshal(&b); err != nil {
   209  		return err
   210  	}
   211  	*job = Job(b)
   212  	return nil
   213  }
   214  
   215  func (job Job) Merge(nextJob *Job) Job {
   216  	if nextJob.Skip {
   217  		return Job{
   218  			Skip: nextJob.Skip,
   219  		}
   220  	}
   221  	if nextJob.Builder != "" {
   222  		job.Builder = nextJob.Builder
   223  	}
   224  	if !nextJob.Run.IsZero() {
   225  		job.Run = nextJob.Run
   226  	}
   227  	if nextJob.Artifacts != nil {
   228  		job.Artifacts = nextJob.Artifacts
   229  	}
   230  	return job
   231  }