github.com/jfrog/jfrog-cli-go@v1.22.1-0.20200318093948-4826ef344ffd/artifactory/commands/utils/configfile.go (about)

     1  package utils
     2  
     3  import (
     4  	"errors"
     5  	"github.com/jfrog/jfrog-cli-go/utils/cliutils"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	"github.com/codegangsta/cli"
    12  	"github.com/jfrog/jfrog-cli-go/artifactory/utils"
    13  	"github.com/jfrog/jfrog-cli-go/utils/config"
    14  	"github.com/jfrog/jfrog-client-go/utils/errorutils"
    15  	"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
    16  	"github.com/jfrog/jfrog-client-go/utils/log"
    17  	"github.com/jfrog/jfrog-client-go/utils/prompt"
    18  	"gopkg.in/yaml.v2"
    19  )
    20  
    21  const BUILD_CONF_VERSION = 1
    22  
    23  const (
    24  	// Common flags
    25  	Global             = "global"
    26  	ResolutionServerId = "server-id-resolve"
    27  	DeploymentServerId = "server-id-deploy"
    28  	ResolutionRepo     = "repo-resolve"
    29  	DeploymentRepo     = "repo-deploy"
    30  
    31  	// Maven flags
    32  	ResolutionReleasesRepo  = "repo-resolve-releases"
    33  	ResolutionSnapshotsRepo = "repo-resolve-snapshots"
    34  	DeploymentReleasesRepo  = "repo-deploy-releases"
    35  	DeploymentSnapshotsRepo = "repo-deploy-snapshots"
    36  
    37  	// Gradle flags
    38  	UsesPlugin          = "uses-plugin"
    39  	UseWrapper          = "use-wrapper"
    40  	DeployMavenDesc     = "deploy-maven-desc"
    41  	DeployIvyDesc       = "deploy-ivy-desc"
    42  	IvyDescPattern      = "ivy-desc-pattern"
    43  	IvyArtifactsPattern = "ivy-artifacts-pattern"
    44  )
    45  
    46  type ConfigFile struct {
    47  	Interactive bool             `yaml:"-"`
    48  	Version     int              `yaml:"version,omitempty"`
    49  	ConfigType  string           `yaml:"type,omitempty"`
    50  	Resolver    utils.Repository `yaml:"resolver,omitempty"`
    51  	Deployer    utils.Repository `yaml:"deployer,omitempty"`
    52  	UsePlugin   bool             `yaml:"usePlugin,omitempty"`
    53  	UseWrapper  bool             `yaml:"useWrapper,omitempty"`
    54  }
    55  
    56  func NewConfigFile(confType utils.ProjectType, c *cli.Context) *ConfigFile {
    57  	configFile := &ConfigFile{
    58  		Version:    BUILD_CONF_VERSION,
    59  		ConfigType: confType.String(),
    60  	}
    61  	configFile.populateConfigFromFlags(c)
    62  	if confType == utils.Maven {
    63  		configFile.populateMavenConfigFromFlags(c)
    64  	} else if confType == utils.Gradle {
    65  		configFile.populateGradleConfigFromFlags(c)
    66  	}
    67  	return configFile
    68  }
    69  
    70  func CreateBuildConfig(c *cli.Context, confType utils.ProjectType) (err error) {
    71  	global := c.Bool(Global)
    72  	projectDir, err := utils.GetProjectDir(global)
    73  	if err != nil {
    74  		return err
    75  	}
    76  	if err = fileutils.CreateDirIfNotExist(projectDir); err != nil {
    77  		return err
    78  	}
    79  	configFilePath := filepath.Join(projectDir, confType.String()+".yaml")
    80  	configFile := NewConfigFile(confType, c)
    81  	if err := configFile.VerifyConfigFile(configFilePath); err != nil {
    82  		return err
    83  	}
    84  	if configFile.Interactive {
    85  		switch confType {
    86  		case utils.Go:
    87  			err = configFile.configGo()
    88  		case utils.Pip:
    89  			err = configFile.configPip()
    90  		case utils.Npm:
    91  			err = configFile.configNpm()
    92  		case utils.Nuget:
    93  			err = configFile.configNuget()
    94  		case utils.Maven:
    95  			err = configFile.configMaven(c)
    96  		case utils.Gradle:
    97  			err = configFile.configGradle(c)
    98  		}
    99  		if err != nil {
   100  			return errorutils.CheckError(err)
   101  		}
   102  	}
   103  	if err = configFile.validateConfig(); err != nil {
   104  		return err
   105  	}
   106  	resBytes, err := yaml.Marshal(&configFile)
   107  	if err != nil {
   108  		return errorutils.CheckError(err)
   109  	}
   110  	if err = ioutil.WriteFile(configFilePath, resBytes, 0644); err != nil {
   111  		return errorutils.CheckError(err)
   112  	}
   113  	log.Info(confType.String() + " build config successfully created.")
   114  	return nil
   115  }
   116  
   117  func isInteractive(c *cli.Context) bool {
   118  	if strings.ToLower(os.Getenv(cliutils.CI)) == "true" {
   119  		return false
   120  	}
   121  	return !isAnyFlagSet(c, ResolutionServerId, ResolutionRepo, DeploymentServerId, DeploymentRepo)
   122  }
   123  
   124  func isAnyFlagSet(c *cli.Context, flagNames ...string) bool {
   125  	for _, flagName := range flagNames {
   126  		if c.IsSet(flagName) {
   127  			return true
   128  		}
   129  	}
   130  	return false
   131  }
   132  
   133  // Populate configuration from cli flags
   134  func (configFile *ConfigFile) populateConfigFromFlags(c *cli.Context) {
   135  	configFile.Resolver.ServerId = c.String(ResolutionServerId)
   136  	configFile.Resolver.Repo = c.String(ResolutionRepo)
   137  	configFile.Deployer.ServerId = c.String(DeploymentServerId)
   138  	configFile.Deployer.Repo = c.String(DeploymentRepo)
   139  	configFile.Interactive = isInteractive(c)
   140  }
   141  
   142  // Populate Maven related configuration from cli flags
   143  func (configFile *ConfigFile) populateMavenConfigFromFlags(c *cli.Context) {
   144  	configFile.Resolver.SnapshotRepo = c.String(ResolutionSnapshotsRepo)
   145  	configFile.Resolver.ReleaseRepo = c.String(ResolutionReleasesRepo)
   146  	configFile.Deployer.SnapshotRepo = c.String(DeploymentSnapshotsRepo)
   147  	configFile.Deployer.ReleaseRepo = c.String(DeploymentReleasesRepo)
   148  	configFile.Interactive = configFile.Interactive && !isAnyFlagSet(c, ResolutionSnapshotsRepo, ResolutionReleasesRepo, DeploymentSnapshotsRepo, DeploymentReleasesRepo)
   149  }
   150  
   151  // Populate Gradle related configuration from cli flags
   152  func (configFile *ConfigFile) populateGradleConfigFromFlags(c *cli.Context) {
   153  	configFile.Deployer.DeployMavenDesc = c.BoolT(DeployMavenDesc)
   154  	configFile.Deployer.DeployIvyDesc = c.BoolT(DeployIvyDesc)
   155  	configFile.Deployer.IvyPattern = defaultIfNotSet(c, IvyDescPattern, "[organization]/[module]/ivy-[revision].xml")
   156  	configFile.Deployer.ArtifactsPattern = defaultIfNotSet(c, IvyArtifactsPattern, "[organization]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]")
   157  	configFile.UsePlugin = c.Bool(UsesPlugin)
   158  	configFile.UseWrapper = c.Bool(UseWrapper)
   159  	configFile.Interactive = configFile.Interactive && !isAnyFlagSet(c, DeployMavenDesc, DeployIvyDesc, IvyDescPattern, IvyArtifactsPattern, UsesPlugin, UseWrapper)
   160  }
   161  
   162  // Verify config file doesn't exist or prompt to override it
   163  func (configFile *ConfigFile) VerifyConfigFile(configFilePath string) error {
   164  	exists, err := fileutils.IsFileExists(configFilePath, false)
   165  	if err != nil {
   166  		return err
   167  	}
   168  	if exists {
   169  		if !configFile.Interactive {
   170  			return nil
   171  		}
   172  		override, err := askYesNo("Configuration file already exists at "+configFilePath+". Override it (y/n) [${default}]? ", "n", "override")
   173  		if err != nil {
   174  			return err
   175  		}
   176  		if !override {
   177  			return errorutils.CheckError(errors.New("Operation canceled."))
   178  		}
   179  		return nil
   180  	}
   181  
   182  	// Create config file to make sure the path is valid
   183  	f, err := os.OpenFile(configFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
   184  	if errorutils.CheckError(err) != nil {
   185  		return err
   186  	}
   187  	f.Close()
   188  	// The file will be written at the end of successful configuration command.
   189  	return errorutils.CheckError(os.Remove(configFilePath))
   190  }
   191  
   192  func (configFile *ConfigFile) configGo() error {
   193  	return configFile.setDeployerResolver()
   194  }
   195  
   196  func (configFile *ConfigFile) configPip() error {
   197  	return configFile.setResolver()
   198  }
   199  
   200  func (configFile *ConfigFile) configNpm() error {
   201  	return configFile.setDeployerResolver()
   202  }
   203  
   204  func (configFile *ConfigFile) configNuget() error {
   205  	return configFile.setResolver()
   206  }
   207  
   208  func (configFile *ConfigFile) configMaven(c *cli.Context) error {
   209  	// Set resolution repositories
   210  	if err := configFile.setResolverId(); err != nil {
   211  		return err
   212  	}
   213  	if configFile.Resolver.ServerId != "" {
   214  		if err := configFile.setRepo(&configFile.Resolver.ReleaseRepo, "Set resolution repository for release dependencies", configFile.Resolver.ServerId, utils.REMOTE); err != nil {
   215  			return err
   216  		}
   217  		if err := configFile.setRepo(&configFile.Resolver.SnapshotRepo, "Set resolution repository for snapshot dependencies", configFile.Resolver.ServerId, utils.REMOTE); err != nil {
   218  			return err
   219  		}
   220  	}
   221  	// Set deployment repositories
   222  	if err := configFile.setDeployerId(); err != nil {
   223  		return err
   224  	}
   225  	if configFile.Deployer.ServerId != "" {
   226  		if err := configFile.setRepo(&configFile.Deployer.ReleaseRepo, "Set repository for release artifacts deployment", configFile.Deployer.ServerId, utils.LOCAL); err != nil {
   227  			return err
   228  		}
   229  		return configFile.setRepo(&configFile.Deployer.SnapshotRepo, "Set repository for snapshot artifacts deployment", configFile.Deployer.ServerId, utils.LOCAL)
   230  	}
   231  	return nil
   232  }
   233  
   234  func (configFile *ConfigFile) configGradle(c *cli.Context) error {
   235  	if err := configFile.setDeployerResolver(); err != nil {
   236  		return err
   237  	}
   238  	if configFile.Deployer.ServerId != "" {
   239  		if err := configFile.setMavenIvyDescriptors(c); err != nil {
   240  			return err
   241  		}
   242  	}
   243  	return configFile.readGradleGlobalConfig(c)
   244  }
   245  
   246  func (configFile *ConfigFile) readGradleGlobalConfig(c *cli.Context) error {
   247  	var err error
   248  	configFile.UsePlugin, err = askYesNo("Is the Gradle Artifactory Plugin already applied in the build script (y/n) [${default}]? ", "n", utils.USE_GRADLE_PLUGIN)
   249  	if err != nil {
   250  		return err
   251  	}
   252  	configFile.UseWrapper, err = askYesNo("Use Gradle wrapper (y/n) [${default}]? ", "n", utils.USE_GRADLE_WRAPPER)
   253  	return err
   254  }
   255  
   256  func (configFile *ConfigFile) setDeployer() error {
   257  	// Set deployer id
   258  	if err := configFile.setDeployerId(); err != nil {
   259  		return err
   260  	}
   261  
   262  	// Set deployment repository
   263  	if configFile.Deployer.ServerId != "" {
   264  		return configFile.setRepo(&configFile.Deployer.Repo, "Set repository for artifacts deployment", configFile.Deployer.ServerId, utils.LOCAL)
   265  	}
   266  	return nil
   267  }
   268  
   269  func (configFile *ConfigFile) setResolver() error {
   270  	// Set resolver id
   271  	if err := configFile.setResolverId(); err != nil {
   272  		return err
   273  	}
   274  	// Set resolution repository
   275  	if configFile.Resolver.ServerId != "" {
   276  		return configFile.setRepo(&configFile.Resolver.Repo, "Set repository for dependencies resolution", configFile.Resolver.ServerId, utils.REMOTE)
   277  	}
   278  	return nil
   279  }
   280  
   281  func (configFile *ConfigFile) setDeployerResolver() error {
   282  	if err := configFile.setResolver(); err != nil {
   283  		return err
   284  	}
   285  	return configFile.setDeployer()
   286  }
   287  
   288  func (configFile *ConfigFile) setResolverId() error {
   289  	return configFile.setServerId(&configFile.Resolver.ServerId, "Resolve dependencies from Artifactory")
   290  }
   291  
   292  func (configFile *ConfigFile) setDeployerId() error {
   293  	return configFile.setServerId(&configFile.Deployer.ServerId, "Deploy project artifacts to Artifactory")
   294  }
   295  
   296  func (configFile *ConfigFile) setServerId(serverId *string, useArtifactoryQuestion string) error {
   297  	var err error
   298  	*serverId, err = readArtifactoryServer(useArtifactoryQuestion + " (y/n) [${default}]? ")
   299  	return err
   300  }
   301  
   302  func (configFile *ConfigFile) setRepo(repo *string, message string, serverId string, repoType utils.RepoType) error {
   303  	var err error
   304  	if *repo == "" {
   305  		*repo, err = readRepo(message+" (press Tab for options): ", serverId, repoType, utils.VIRTUAL)
   306  	}
   307  	return err
   308  }
   309  
   310  func (configFile *ConfigFile) setMavenIvyDescriptors(c *cli.Context) error {
   311  	var err error
   312  	configFile.Deployer.DeployMavenDesc, err = askYesNo("Deploy Maven descriptors (y/n) [${default}]? ", "n", utils.MAVEN_DESCRIPTOR)
   313  	if err != nil {
   314  		return err
   315  	}
   316  
   317  	configFile.Deployer.DeployIvyDesc, err = askYesNo("Deploy Ivy descriptors (y/n) [${default}]? ", "n", utils.IVY_DESCRIPTOR)
   318  	if err != nil {
   319  		return err
   320  	}
   321  
   322  	if configFile.Deployer.DeployIvyDesc {
   323  		configFile.Deployer.IvyPattern, err = askString("Set Ivy descriptor pattern [${default}]:", "[organization]/[module]/ivy-[revision].xml", utils.IVY_PATTERN)
   324  		if err != nil {
   325  			return err
   326  		}
   327  		configFile.Deployer.ArtifactsPattern, err = askString("Set Ivy artifact pattern [${default}]:", "[organization]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]", utils.ARTIFACT_PATTERN)
   328  	}
   329  	return err
   330  }
   331  
   332  // Check correctness of spec file configuration
   333  func (configFile *ConfigFile) validateConfig() error {
   334  	resolver := configFile.Resolver
   335  	releaseRepo := resolver.ReleaseRepo
   336  	snapshotRepo := resolver.SnapshotRepo
   337  	if resolver.ServerId != "" {
   338  		if resolver.Repo == "" && releaseRepo == "" && snapshotRepo == "" {
   339  			return errorutils.CheckError(errors.New("Resolution repository/ies must be set."))
   340  		}
   341  		if (releaseRepo == "" && snapshotRepo != "") || (releaseRepo != "" && snapshotRepo == "") {
   342  			return errorutils.CheckError(errors.New("Resolution snapshot and release repositories must be set."))
   343  		}
   344  	} else {
   345  		if resolver.Repo != "" || releaseRepo != "" || snapshotRepo != "" {
   346  			return errorutils.CheckError(errors.New("Resolver server ID must be set."))
   347  		}
   348  	}
   349  	deployer := configFile.Deployer
   350  	releaseRepo = deployer.ReleaseRepo
   351  	snapshotRepo = deployer.SnapshotRepo
   352  	if deployer.ServerId != "" {
   353  		if deployer.Repo == "" && releaseRepo == "" && snapshotRepo == "" {
   354  			return errorutils.CheckError(errors.New("Deployment repository/ies must be set."))
   355  		}
   356  		if (releaseRepo == "" && snapshotRepo != "") || (releaseRepo != "" && snapshotRepo == "") {
   357  			return errorutils.CheckError(errors.New("Deployment snapshot and release repositories must be set."))
   358  		}
   359  	} else {
   360  		if deployer.Repo != "" || releaseRepo != "" || snapshotRepo != "" {
   361  			return errorutils.CheckError(errors.New("Deployer server ID must be set."))
   362  		}
   363  	}
   364  	return nil
   365  }
   366  
   367  // Get Artifactory serverId from the user. If useArtifactoryQuestion is not empty, ask first whether to use artifactory.
   368  func readArtifactoryServer(useArtifactoryQuestion string) (string, error) {
   369  	// Get all Artifactory servers
   370  	serversIds, defaultServer, err := getServersIdAndDefault()
   371  	if err != nil {
   372  		return "", err
   373  	}
   374  	if len(serversIds) == 0 {
   375  		return "", errorutils.CheckError(errors.New("No Artifactory servers configured. Use the 'jfrog rt c' command to set the Artifactory server details."))
   376  	}
   377  
   378  	// Ask whether to use artifactory
   379  	if useArtifactoryQuestion != "" {
   380  		useArtifactory, err := askYesNo(useArtifactoryQuestion, "y", "useArtifactory")
   381  		if err != nil || !useArtifactory {
   382  			return "", err
   383  		}
   384  	}
   385  
   386  	return askAutocomplete("Set Artifactory server ID (press Tab for options) [${default}]: ", "Server does not exist. Please set a valid server ID.", serversIds, defaultServer, utils.SERVER_ID)
   387  }
   388  
   389  func readRepo(msg string, serverId string, repoTypes ...utils.RepoType) (string, error) {
   390  	availableRepos, err := getRepositories(serverId, repoTypes...)
   391  	if err != nil {
   392  		// If there are no available repos pass empty array.
   393  		availableRepos = []string{}
   394  	}
   395  	repo := &prompt.Autocomplete{
   396  		Msg:     msg,
   397  		Options: availableRepos,
   398  		Label:   utils.ProjectConfigRepo,
   399  	}
   400  	if len(availableRepos) > 0 {
   401  		repo.ConfirmationMsg = "No such repository, continue anyway (y/n) [${default}]? "
   402  		repo.ConfirmationDefault = "n"
   403  	} else {
   404  		repo.ErrMsg = "Repository name cannot be empty."
   405  	}
   406  	if err = repo.Read(); err != nil {
   407  		return "", err
   408  	}
   409  	return repo.GetResults().GetString(utils.ProjectConfigRepo), nil
   410  }
   411  
   412  func getServersIdAndDefault() ([]string, string, error) {
   413  	allConfigs, err := config.GetAllArtifactoryConfigs()
   414  	if err != nil {
   415  		return nil, "", err
   416  	}
   417  	var defaultVal string
   418  	var serversId []string
   419  	for _, v := range allConfigs {
   420  		if v.IsDefault {
   421  			defaultVal = v.ServerId
   422  		}
   423  		serversId = append(serversId, v.ServerId)
   424  	}
   425  	return serversId, defaultVal, nil
   426  }
   427  
   428  func getRepositories(serverId string, repoTypes ...utils.RepoType) ([]string, error) {
   429  	artDetails, err := config.GetArtifactoryConf(serverId)
   430  	if err != nil {
   431  		return nil, err
   432  	}
   433  
   434  	artAuth, err := artDetails.CreateArtAuthConfig()
   435  	if err != nil {
   436  		return nil, err
   437  	}
   438  
   439  	return utils.GetRepositories(artAuth, repoTypes...)
   440  }
   441  
   442  func askYesNo(message string, defaultStr string, label string) (bool, error) {
   443  	question := &prompt.YesNo{
   444  		Msg:     message,
   445  		Default: defaultStr,
   446  		Label:   label,
   447  	}
   448  	if err := question.Read(); err != nil {
   449  		return false, errorutils.CheckError(err)
   450  	}
   451  	return question.Result.GetBool(label), nil
   452  }
   453  
   454  func askString(message string, defaultStr string, label string) (string, error) {
   455  	question := &prompt.Simple{
   456  		Msg:     message,
   457  		Default: defaultStr,
   458  		Label:   label,
   459  	}
   460  	if err := question.Read(); err != nil {
   461  		return "", errorutils.CheckError(err)
   462  	}
   463  	return question.Result.GetString(label), nil
   464  }
   465  
   466  func askAutocomplete(msg string, errMsg string, options []string, defaultStr string, label string) (string, error) {
   467  	question := &prompt.Autocomplete{
   468  		Msg:     msg,
   469  		ErrMsg:  errMsg,
   470  		Options: options,
   471  		Default: defaultStr,
   472  		Label:   label,
   473  	}
   474  	if err := question.Read(); err != nil {
   475  		return "", errorutils.CheckError(err)
   476  	}
   477  	return question.Result.GetString(label), nil
   478  }
   479  
   480  func defaultIfNotSet(c *cli.Context, flagName string, defaultValue string) string {
   481  	if c.IsSet(flagName) {
   482  		return c.String(flagName)
   483  	}
   484  	return defaultValue
   485  }