github.com/drone/runner-go@v1.12.0/environ/environ.go (about) 1 // Copyright 2019 Drone.IO Inc. All rights reserved. 2 // Use of this source code is governed by the Polyform License 3 // that can be found in the LICENSE file. 4 5 // Package environ provides utilities for generating environment 6 // variables for a build pipeline. 7 package environ 8 9 import ( 10 "fmt" 11 "regexp" 12 "sort" 13 "strings" 14 "time" 15 16 "github.com/drone/drone-go/drone" 17 ) 18 19 // regular expression to extract the pull request number 20 // from the git ref (e.g. refs/pulls/{d}/head) 21 var re = regexp.MustCompile("\\d+") 22 23 // System returns a set of environment variables containing 24 // system metadata. 25 func System(system *drone.System) map[string]string { 26 return map[string]string{ 27 "CI": "true", 28 "DRONE": "true", 29 "DRONE_SYSTEM_PROTO": system.Proto, 30 "DRONE_SYSTEM_HOST": system.Host, 31 "DRONE_SYSTEM_HOSTNAME": system.Host, 32 "DRONE_SYSTEM_VERSION": fmt.Sprint(system.Version), 33 } 34 } 35 36 // Repo returns a set of environment variables containing 37 // repository metadata. 38 func Repo(repo *drone.Repo) map[string]string { 39 return map[string]string{ 40 "DRONE_REPO": repo.Slug, 41 "DRONE_REPO_SCM": repo.SCM, 42 "DRONE_REPO_OWNER": repo.Namespace, 43 "DRONE_REPO_NAMESPACE": repo.Namespace, 44 "DRONE_REPO_NAME": repo.Name, 45 "DRONE_REPO_LINK": repo.Link, 46 "DRONE_REPO_BRANCH": repo.Branch, 47 "DRONE_REMOTE_URL": repo.HTTPURL, 48 "DRONE_GIT_HTTP_URL": repo.HTTPURL, 49 "DRONE_GIT_SSH_URL": repo.SSHURL, 50 "DRONE_REPO_VISIBILITY": repo.Visibility, 51 "DRONE_REPO_PRIVATE": fmt.Sprint(repo.Private), 52 53 // these are legacy configuration parameters for backward 54 // compatibility with drone 0.8. These are deprecated and 55 // should not be relied upon going forward. 56 "CI_REPO": repo.Slug, 57 "CI_REPO_NAME": repo.Slug, 58 "CI_REPO_LINK": repo.Link, 59 "CI_REPO_REMOTE": repo.HTTPURL, 60 "CI_REMOTE_URL": repo.HTTPURL, 61 "CI_REPO_PRIVATE": fmt.Sprint(repo.Private), 62 } 63 } 64 65 // Stage returns a set of environment variables containing 66 // stage metadata. 67 func Stage(stage *drone.Stage) map[string]string { 68 env := map[string]string{ 69 "DRONE_STAGE_KIND": stage.Kind, 70 "DRONE_STAGE_TYPE": stage.Type, 71 "DRONE_STAGE_NAME": stage.Name, 72 "DRONE_STAGE_NUMBER": fmt.Sprint(stage.Number), 73 "DRONE_STAGE_MACHINE": stage.Machine, 74 "DRONE_STAGE_OS": stage.OS, 75 "DRONE_STAGE_ARCH": stage.Arch, 76 "DRONE_STAGE_VARIANT": stage.Variant, 77 "DRONE_STAGE_VERSION": fmt.Sprint(stage.Version), 78 "DRONE_STAGE_STATUS": "success", 79 "DRONE_STAGE_STARTED": fmt.Sprint(stage.Started), 80 "DRONE_STAGE_FINISHED": fmt.Sprint(stage.Stopped), 81 "DRONE_STAGE_DEPENDS_ON": strings.Join(stage.DependsOn, ","), 82 "DRONE_CARD_PATH": "/dev/stdout", 83 } 84 if isStageFailing(stage) { 85 env["DRONE_STAGE_STATUS"] = "failure" 86 env["DRONE_FAILED_STEPS"] = strings.Join(failedSteps(stage), ",") 87 } 88 if stage.Started == 0 { 89 env["DRONE_STAGE_STARTED"] = fmt.Sprint(time.Now().Unix()) 90 } 91 if stage.Stopped == 0 { 92 env["DRONE_STAGE_FINISHED"] = fmt.Sprint(time.Now().Unix()) 93 } 94 return env 95 } 96 97 // Step returns a set of environment variables containing the 98 // step metadata. 99 func Step(step *drone.Step) map[string]string { 100 return map[string]string{ 101 "DRONE_STEP_NAME": step.Name, 102 "DRONE_STEP_NUMBER": fmt.Sprint(step.Number), 103 } 104 } 105 106 // StepArgs returns a set of environment variables containing 107 // the step name and number. 108 func StepArgs(name string, number int64) map[string]string { 109 return map[string]string{ 110 "DRONE_STEP_NAME": name, 111 "DRONE_STEP_NUMBER": fmt.Sprint(number), 112 } 113 } 114 115 // StepName returns a set of environment variables containing 116 // only the step name. 117 func StepName(name string) map[string]string { 118 return map[string]string{ 119 "DRONE_STEP_NAME": name, 120 } 121 } 122 123 // Build returns a set of environment variables containing 124 // build metadata. 125 func Build(build *drone.Build) map[string]string { 126 env := map[string]string{ 127 "DRONE_BRANCH": build.Target, 128 "DRONE_SOURCE_BRANCH": build.Source, 129 "DRONE_TARGET_BRANCH": build.Target, 130 "DRONE_COMMIT": build.After, 131 "DRONE_COMMIT_SHA": build.After, 132 "DRONE_COMMIT_BEFORE": build.Before, 133 "DRONE_COMMIT_AFTER": build.After, 134 "DRONE_COMMIT_REF": build.Ref, 135 "DRONE_COMMIT_BRANCH": build.Target, 136 "DRONE_COMMIT_LINK": build.Link, 137 "DRONE_COMMIT_MESSAGE": build.Message, 138 "DRONE_COMMIT_AUTHOR": build.Author, 139 "DRONE_COMMIT_AUTHOR_EMAIL": build.AuthorEmail, 140 "DRONE_COMMIT_AUTHOR_AVATAR": build.AuthorAvatar, 141 "DRONE_COMMIT_AUTHOR_NAME": build.AuthorName, 142 "DRONE_BUILD_NUMBER": fmt.Sprint(build.Number), 143 "DRONE_BUILD_PARENT": fmt.Sprint(build.Parent), 144 "DRONE_BUILD_EVENT": build.Event, 145 "DRONE_BUILD_ACTION": build.Action, 146 "DRONE_BUILD_STATUS": "success", 147 "DRONE_BUILD_DEBUG": fmt.Sprint(build.Debug), 148 "DRONE_BUILD_CREATED": fmt.Sprint(build.Created), 149 "DRONE_BUILD_STARTED": fmt.Sprint(build.Started), 150 "DRONE_BUILD_FINISHED": fmt.Sprint(build.Finished), 151 "DRONE_DEPLOY_TO": build.Deploy, 152 "DRONE_DEPLOY_ID": fmt.Sprint(build.DeployID), 153 "DRONE_BUILD_TRIGGER": build.Trigger, 154 155 // these are legacy configuration parameters for backward 156 // compatibility with drone 0.8. These are deprecated and 157 // should not be relied upon going forward. 158 "CI_BUILD_NUMBER": fmt.Sprint(build.Number), 159 "CI_PARENT_BUILD_NUMBER": fmt.Sprint(build.Parent), 160 "CI_BUILD_CREATED": fmt.Sprint(build.Created), 161 "CI_BUILD_STARTED": fmt.Sprint(build.Started), 162 "CI_BUILD_FINISHED": fmt.Sprint(build.Finished), 163 "CI_BUILD_STATUS": build.Status, 164 "CI_BUILD_EVENT": build.Event, 165 "CI_BUILD_LINK": build.Link, 166 "CI_BUILD_TARGET": build.Deploy, 167 "CI_COMMIT_SHA": build.After, 168 "CI_COMMIT_REF": build.Ref, 169 "CI_COMMIT_BRANCH": build.Target, 170 "CI_COMMIT_MESSAGE": build.Message, 171 "CI_COMMIT_AUTHOR": build.Author, 172 "CI_COMMIT_AUTHOR_NAME": build.AuthorName, 173 "CI_COMMIT_AUTHOR_EMAIL": build.AuthorEmail, 174 "CI_COMMIT_AUTHOR_AVATAR": build.AuthorAvatar, 175 } 176 if isBuildFailing(build) { 177 env["DRONE_BUILD_STATUS"] = "failure" 178 env["DRONE_FAILED_STAGES"] = strings.Join(failedStages(build), ",") 179 } 180 if build.Started == 0 { 181 env["DRONE_BUILD_STARTED"] = fmt.Sprint(time.Now().Unix()) 182 } 183 if build.Finished == 0 { 184 env["DRONE_BUILD_FINISHED"] = fmt.Sprint(time.Now().Unix()) 185 } 186 if build.Event == drone.EventPullRequest { 187 env["DRONE_PULL_REQUEST"] = re.FindString(build.Ref) 188 env["DRONE_PULL_REQUEST_TITLE"] = build.Title 189 } 190 if strings.HasPrefix(build.Ref, "refs/tags/") { 191 tag := strings.TrimPrefix(build.Ref, "refs/tags/") 192 env["DRONE_TAG"] = tag 193 copyenv(versions(tag), env) 194 copyenv(calversions(tag), env) 195 } 196 return env 197 } 198 199 // Link returns a set of environment variables containing 200 // resource urls to the build. 201 func Link(repo *drone.Repo, build *drone.Build, system *drone.System) map[string]string { 202 return map[string]string{ 203 "DRONE_BUILD_LINK": fmt.Sprintf( 204 "%s://%s/%s/%d", 205 system.Proto, 206 system.Host, 207 repo.Slug, 208 build.Number, 209 ), 210 } 211 } 212 213 // Netrc returns a set of environment variables containing 214 // the netrc file and credentials. 215 func Netrc(netrc *drone.Netrc) map[string]string { 216 env := map[string]string{} 217 if netrc != nil && netrc.Machine != "" { 218 env["DRONE_NETRC_MACHINE"] = netrc.Machine 219 env["DRONE_NETRC_USERNAME"] = netrc.Login 220 env["DRONE_NETRC_PASSWORD"] = netrc.Password 221 env["DRONE_NETRC_FILE"] = fmt.Sprintf( 222 "machine %s login %s password %s", 223 netrc.Machine, 224 netrc.Login, 225 netrc.Password, 226 ) 227 } 228 return env 229 } 230 231 // Combine is a helper function combines one or more maps of 232 // environment variables into a single map. 233 func Combine(env ...map[string]string) map[string]string { 234 c := map[string]string{} 235 for _, e := range env { 236 for k, v := range e { 237 c[k] = v 238 } 239 } 240 return c 241 } 242 243 // Slice is a helper function that converts a map of environment 244 // variables to a slice of string values in key=value format. 245 func Slice(env map[string]string) []string { 246 s := []string{} 247 for k, v := range env { 248 s = append(s, k+"="+v) 249 } 250 sort.Strings(s) 251 return s 252 } 253 254 // copyenv copies environment variables from the source map 255 // to the destination map. 256 func copyenv(src, dst map[string]string) { 257 for k, v := range src { 258 dst[k] = v 259 } 260 } 261 262 // helper function returns true of the build is failing. 263 func isBuildFailing(build *drone.Build) bool { 264 if build.Status == drone.StatusError || 265 build.Status == drone.StatusFailing || 266 build.Status == drone.StatusKilled { 267 return true 268 } 269 for _, stage := range build.Stages { 270 if stage.Status == drone.StatusError || 271 stage.Status == drone.StatusFailing || 272 stage.Status == drone.StatusKilled { 273 return true 274 } 275 } 276 return false 277 } 278 279 // helper function returns true of the stage is failing. 280 func isStageFailing(stage *drone.Stage) bool { 281 if stage.Status == drone.StatusError || 282 stage.Status == drone.StatusFailing || 283 stage.Status == drone.StatusKilled { 284 return true 285 } 286 for _, step := range stage.Steps { 287 if step.ErrIgnore && step.Status == drone.StatusFailing { 288 continue 289 } 290 if step.Status == drone.StatusError || 291 step.Status == drone.StatusFailing || 292 step.Status == drone.StatusKilled { 293 return true 294 } 295 } 296 return false 297 } 298 299 // helper function returns the failed steps. 300 func failedSteps(stage *drone.Stage) []string { 301 var steps []string 302 for _, step := range stage.Steps { 303 if step.ErrIgnore && step.Status == drone.StatusFailing { 304 continue 305 } 306 if step.Status == drone.StatusError || 307 step.Status == drone.StatusFailing || 308 step.Status == drone.StatusKilled { 309 steps = append(steps, step.Name) 310 } 311 } 312 return steps 313 } 314 315 // helper function returns the failed stages. 316 func failedStages(build *drone.Build) []string { 317 var stages []string 318 for _, stage := range build.Stages { 319 if stage.Status == drone.StatusError || 320 stage.Status == drone.StatusFailing || 321 stage.Status == drone.StatusKilled { 322 stages = append(stages, stage.Name) 323 } 324 } 325 return stages 326 }