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 }