github.com/olli-ai/jx/v2@v2.0.400-0.20210921045218-14731b4dd448/pkg/cmd/step/step_release.go (about) 1 package step 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 8 "github.com/olli-ai/jx/v2/pkg/cmd/step/git/credentials" 9 "github.com/olli-ai/jx/v2/pkg/cmd/step/post" 10 11 "github.com/olli-ai/jx/v2/pkg/cmd/opts/step" 12 13 "github.com/olli-ai/jx/v2/pkg/cmd/helper" 14 "github.com/olli-ai/jx/v2/pkg/cmd/promote" 15 16 "github.com/jenkins-x/jx-logging/pkg/log" 17 "github.com/olli-ai/jx/v2/pkg/cmd/opts" 18 helm_cmd "github.com/olli-ai/jx/v2/pkg/cmd/step/helm" 19 "github.com/olli-ai/jx/v2/pkg/kube" 20 "github.com/olli-ai/jx/v2/pkg/util" 21 "github.com/spf13/cobra" 22 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 ) 24 25 // StepReleaseOptions contains the CLI arguments 26 type StepReleaseOptions struct { 27 step.StepOptions 28 29 DockerRegistry string 30 Organisation string 31 Application string 32 Version string 33 GitUsername string 34 GitEmail string 35 Dir string 36 XdgConfigHome string 37 NoBatch bool 38 39 // promote flags 40 Build string 41 Timeout string 42 PullRequestPollTime string 43 LocalHelmRepoName string 44 HelmRepositoryURL string 45 } 46 47 const ( 48 optionPullRequestPollTime = "pull-request-poll-time" 49 ) 50 51 // NewCmdStep Steps a command object for the "step" command 52 func NewCmdStepRelease(commonOpts *opts.CommonOptions) *cobra.Command { 53 options := &StepReleaseOptions{ 54 StepOptions: step.StepOptions{ 55 CommonOptions: commonOpts, 56 }, 57 } 58 59 cmd := &cobra.Command{ 60 Use: "release", 61 Short: "performs a release on the current Git repository", 62 Run: func(cmd *cobra.Command, args []string) { 63 options.Cmd = cmd 64 options.Args = args 65 err := options.Run() 66 helper.CheckErr(err) 67 }, 68 } 69 70 cmd.Flags().StringVarP(&options.DockerRegistry, "docker-registry", "r", "", "the Docker registry host or host:port to use. If not specified it is loaded from the `docker-registry` ConfigMap") 71 cmd.Flags().StringVarP(&options.Organisation, "organisation", "o", "", "the Docker organisation for the generated Docker image") 72 cmd.Flags().StringVarP(&options.Application, "application", "a", "", "the Docker application image name") 73 cmd.Flags().StringVarP(&options.GitUsername, "git-username", "u", "", "The Git username to configure if there is none already setup") 74 cmd.Flags().StringVarP(&options.GitEmail, "git-email", "e", "", "The Git email address to configure if there is none already setup") 75 cmd.Flags().StringVarP(&options.XdgConfigHome, "xdg-config-home", "", "/home/jenkins", "The home directory where git config is setup") 76 cmd.Flags().BoolVarP(&options.NoBatch, "no-batch", "", false, "Whether to disable batch mode") 77 cmd.Flags().StringVarP(&options.Timeout, opts.OptionTimeout, "t", "1h", "The timeout to wait for the promotion to succeed in the underlying Environment. The command fails if the timeout is exceeded or the promotion does not complete") 78 cmd.Flags().StringVarP(&options.PullRequestPollTime, optionPullRequestPollTime, "", "20s", "Poll time when waiting for a Pull Request to merge") 79 cmd.Flags().StringVarP(&options.LocalHelmRepoName, "helm-repo-name", "", kube.LocalHelmRepoName, "The name of the helm repository that contains the app") 80 cmd.Flags().StringVarP(&options.HelmRepositoryURL, "helm-repo-url", "", "", "The Helm Repository URL to use for the App") 81 cmd.Flags().StringVarP(&options.Build, "build", "", "", "The Build number which is used to update the PipelineActivity. If not specified its defaulted from the '$BUILD_NUMBER' environment variable") 82 83 return cmd 84 } 85 86 // Run implements this command 87 func (o *StepReleaseOptions) Run() error { 88 o.BatchMode = !o.NoBatch 89 err := o.RunCommandVerbose("git", "config", "--global", "credential.helper", "store") 90 if err != nil { 91 return err 92 } 93 if o.XdgConfigHome != "" { 94 if os.Getenv("XDG_CONFIG_HOME") == "" { 95 err = o.Setenv("XDG_CONFIG_HOME", o.XdgConfigHome) 96 if err != nil { 97 return err 98 } 99 } 100 } 101 102 stepGitCredentialsOptions := &credentials.StepGitCredentialsOptions{ 103 StepOptions: o.StepOptions, 104 } 105 err = stepGitCredentialsOptions.Run() 106 if err != nil { 107 return fmt.Errorf("Failed to setup Git credentials: %s", err) 108 } 109 dir := o.Dir 110 gitUser, err := o.Git().Username(dir) 111 if err != nil || gitUser == "" { 112 gitUser = o.GitUsername 113 if gitUser == "" { 114 gitUser, _ = o.GetUsername("") 115 } 116 if gitUser == "" { 117 gitUser = util.DefaultGitUserName 118 } 119 err = o.Git().SetUsername(dir, gitUser) 120 if err != nil { 121 return fmt.Errorf("Failed to set Git user %s: %s", gitUser, err) 122 } 123 } 124 gitEmail, err := o.Git().Email(dir) 125 if err != nil || gitEmail == "" { 126 gitEmail = o.GitEmail 127 if gitEmail == "" { 128 gitEmail = util.DefaultGitUserEmail 129 } 130 err = o.Git().SetEmail(dir, gitEmail) 131 if err != nil { 132 return fmt.Errorf("Failed to set Git email %s: %s", gitEmail, err) 133 } 134 } 135 136 if o.DockerRegistry == "" { 137 o.DockerRegistry = os.Getenv("DOCKER_REGISTRY") 138 } 139 if o.Organisation == "" { 140 o.Organisation = os.Getenv("ORG") 141 } 142 if o.Application == "" { 143 o.Application = os.Getenv("APP_NAME") 144 } 145 if o.DockerRegistry == "" { 146 o.DockerRegistry, err = o.loadDockerRegistry() 147 if err != nil { 148 return err 149 } 150 } 151 if o.Organisation == "" || o.Application == "" { 152 gitInfo, err := o.FindGitInfo("") 153 if err != nil { 154 return err 155 } 156 if o.Organisation == "" { 157 o.Organisation = gitInfo.Organisation 158 } 159 if o.Application == "" { 160 o.Application = gitInfo.Name 161 } 162 } 163 err = o.Setenv("DOCKER_REGISTRY", o.DockerRegistry) 164 if err != nil { 165 return err 166 } 167 err = o.Setenv("ORG", o.Organisation) 168 if err != nil { 169 return err 170 } 171 err = o.Setenv("APP_NAME", o.Application) 172 if err != nil { 173 return err 174 } 175 176 stepNextVersionOptions := &StepNextVersionOptions{ 177 StepOptions: o.StepOptions, 178 } 179 if o.isNode() { 180 stepNextVersionOptions.Filename = packagejson 181 /* 182 } else if o.isMaven() { 183 stepNextVersionOptions.Filename = pomxml 184 */ 185 } else { 186 stepNextVersionOptions.UseGitTagOnly = true 187 } 188 err = stepNextVersionOptions.Run() 189 if err != nil { 190 return fmt.Errorf("Failed to create next version: %s", err) 191 } 192 o.Version = stepNextVersionOptions.NewVersion 193 err = o.Setenv("VERSION", o.Version) 194 if err != nil { 195 return err 196 } 197 198 err = o.updateVersionInSource() 199 if err != nil { 200 return fmt.Errorf("Failed to update version in source: %s", err) 201 } 202 203 chartsDir := filepath.Join("charts", o.Application) 204 chartExists, err := util.DirExists(chartsDir) 205 if err != nil { 206 return fmt.Errorf("Failed to find chart folder: %s", err) 207 } 208 209 stepTagOptions := &StepTagOptions{ 210 StepOptions: o.StepOptions, 211 } 212 if chartExists { 213 stepTagOptions.Flags.ChartsDir = chartsDir 214 stepTagOptions.Flags.ChartValueRepository = fmt.Sprintf("%s/%s/%s", o.DockerRegistry, o.Organisation, o.Application) 215 } 216 stepTagOptions.Flags.Version = o.Version 217 err = stepTagOptions.Run() 218 if err != nil { 219 return fmt.Errorf("Failed to tag source: %s", err) 220 } 221 222 err = o.buildSource() 223 if err != nil { 224 return err 225 } 226 err = o.RunCommandVerbose("skaffold", "build", "-f", "skaffold.yaml") 227 if err != nil { 228 return fmt.Errorf("Failed to run skaffold: %s", err) 229 } 230 231 imageName := fmt.Sprintf("%s/%s/%s:%s", o.DockerRegistry, o.Organisation, o.Application, o.Version) 232 233 stepPostBuildOptions := &post.StepPostBuildOptions{ 234 StepOptions: o.StepOptions, 235 FullImageName: imageName, 236 } 237 err = stepPostBuildOptions.Run() 238 if err != nil { 239 return fmt.Errorf("Failed to run post build step: %s", err) 240 } 241 242 // now lets promote from the charts dir... 243 if chartExists { 244 err = o.releaseAndPromoteChart(chartsDir) 245 if err != nil { 246 return fmt.Errorf("Failed to promote: %s", err) 247 } 248 } else { 249 log.Logger().Infof("No charts directory %s so not promoting", util.ColorInfo(chartsDir)) 250 } 251 252 return nil 253 } 254 255 func (o *StepReleaseOptions) updateVersionInSource() error { 256 if o.isMaven() { 257 return o.RunCommandVerbose("mvn", "versions:set", "-DnewVersion="+o.Version) 258 } 259 return nil 260 } 261 262 func (o *StepReleaseOptions) buildSource() error { 263 if o.isMaven() { 264 return o.RunCommandVerbose("mvn", "clean", "deploy") 265 } 266 return nil 267 268 } 269 270 func (o *StepReleaseOptions) loadDockerRegistry() (string, error) { 271 kubeClient, curNs, err := o.KubeClientAndNamespace() 272 if err != nil { 273 return "", err 274 } 275 ns, _, err := kube.GetDevNamespace(kubeClient, curNs) 276 if err != nil { 277 return "", err 278 } 279 280 configMapName := kube.ConfigMapJenkinsDockerRegistry 281 cm, err := kubeClient.CoreV1().ConfigMaps(ns).Get(configMapName, metav1.GetOptions{}) 282 if err != nil { 283 return "", fmt.Errorf("Could not find ConfigMap %s in namespace %s: %s", configMapName, ns, err) 284 } 285 if cm.Data != nil { 286 dockerRegistry := cm.Data["docker.registry"] 287 if dockerRegistry != "" { 288 return dockerRegistry, nil 289 } 290 } 291 return "", fmt.Errorf("Could not find the docker.registry property in the ConfigMap: %s", configMapName) 292 } 293 294 func (o *StepReleaseOptions) releaseAndPromoteChart(dir string) error { 295 err := os.Chdir(dir) 296 if err != nil { 297 return fmt.Errorf("Failed to change to directory %s: %s", dir, err) 298 } 299 300 stepChangelogOptions := &StepChangelogOptions{ 301 StepOptions: o.StepOptions, 302 Build: o.Build, 303 } 304 err = stepChangelogOptions.Run() 305 if err != nil { 306 return fmt.Errorf("Failed to generate changelog: %s", err) 307 } 308 309 if o.HelmRepositoryURL == "" { 310 o.HelmRepositoryURL = o.DefaultChartRepositoryURL() 311 } 312 313 stepHelmReleaseOptions := &helm_cmd.StepHelmReleaseOptions{ 314 StepHelmOptions: helm_cmd.StepHelmOptions{ 315 StepOptions: o.StepOptions, 316 }, 317 } 318 err = stepHelmReleaseOptions.Run() 319 if err != nil { 320 return fmt.Errorf("Failed to release helm chart: %s", err) 321 } 322 323 promoteOptions := promote.PromoteOptions{ 324 CommonOptions: o.CommonOptions, 325 AllAutomatic: true, 326 Timeout: o.Timeout, 327 PullRequestPollTime: o.PullRequestPollTime, 328 Version: o.Version, 329 LocalHelmRepoName: o.LocalHelmRepoName, 330 HelmRepositoryURL: o.HelmRepositoryURL, 331 Build: o.Build, 332 } 333 promoteOptions.BatchMode = true 334 return promoteOptions.Run() 335 } 336 337 func (o *StepReleaseOptions) isMaven() bool { 338 exists, err := util.FileExists("pom.xml") 339 return exists && err == nil 340 } 341 342 func (o *StepReleaseOptions) isNode() bool { 343 exists, err := util.FileExists("package.json") 344 return exists && err == nil 345 } 346 347 func (o *StepReleaseOptions) Setenv(key string, value string) error { 348 err := os.Setenv(key, value) 349 if err != nil { 350 return fmt.Errorf("Failed to set environment variable %s=%s: %s", key, value, err) 351 } 352 return nil 353 }