github.com/jenkins-x/jx/v2@v2.1.155/pkg/cmd/step/step_release.go (about)

     1  package step
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  
     8  	"github.com/jenkins-x/jx/v2/pkg/cmd/step/git/credentials"
     9  	"github.com/jenkins-x/jx/v2/pkg/cmd/step/post"
    10  
    11  	"github.com/jenkins-x/jx/v2/pkg/cmd/opts/step"
    12  
    13  	"github.com/jenkins-x/jx/v2/pkg/cmd/helper"
    14  	"github.com/jenkins-x/jx/v2/pkg/cmd/promote"
    15  
    16  	"github.com/jenkins-x/jx-logging/pkg/log"
    17  	"github.com/jenkins-x/jx/v2/pkg/cmd/opts"
    18  	helm_cmd "github.com/jenkins-x/jx/v2/pkg/cmd/step/helm"
    19  	"github.com/jenkins-x/jx/v2/pkg/kube"
    20  	"github.com/jenkins-x/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  }