github.com/jfrog/jfrog-cli-core/v2@v2.51.0/common/commands/configfile.go (about)

     1  package commands
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
    10  	"github.com/jfrog/jfrog-cli-core/v2/common/project"
    11  	"github.com/jfrog/jfrog-cli-core/v2/utils/config"
    12  	"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
    13  	"github.com/jfrog/jfrog-cli-core/v2/utils/ioutils"
    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/urfave/cli"
    18  	"golang.org/x/text/cases"
    19  	"golang.org/x/text/language"
    20  	"gopkg.in/yaml.v3"
    21  )
    22  
    23  const BuildConfVersion = 1
    24  
    25  const (
    26  	// Common flags
    27  	global             = "global"
    28  	resolutionServerId = "server-id-resolve"
    29  	deploymentServerId = "server-id-deploy"
    30  	resolutionRepo     = "repo-resolve"
    31  	deploymentRepo     = "repo-deploy"
    32  
    33  	// Maven flags
    34  	resolutionReleasesRepo  = "repo-resolve-releases"
    35  	resolutionSnapshotsRepo = "repo-resolve-snapshots"
    36  	deploymentReleasesRepo  = "repo-deploy-releases"
    37  	deploymentSnapshotsRepo = "repo-deploy-snapshots"
    38  	includePatterns         = "include-patterns"
    39  	excludePatterns         = "exclude-patterns"
    40  
    41  	// Gradle flags
    42  	usesPlugin          = "uses-plugin"
    43  	useWrapper          = "use-wrapper"
    44  	deployMavenDesc     = "deploy-maven-desc"
    45  	deployIvyDesc       = "deploy-ivy-desc"
    46  	ivyDescPattern      = "ivy-desc-pattern"
    47  	ivyArtifactsPattern = "ivy-artifacts-pattern"
    48  
    49  	// Nuget flags
    50  	nugetV2 = "nuget-v2"
    51  
    52  	// Default values
    53  	defaultIvyDescPattern      = "[organization]/[module]/ivy-[revision].xml"
    54  	defaultIvyArtifactsPattern = "[organization]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]"
    55  
    56  	// Errors
    57  	resolutionErrorPrefix      = "[Resolution]: "
    58  	deploymentErrorPrefix      = "[Deployment]: "
    59  	setServerIdError           = "server ID must be set. Use the --server-id-resolve/deploy flag or configure a default server using 'jfrog c add' and 'jfrog c use' commands. "
    60  	setRepositoryError         = "repository/ies must be set. "
    61  	setSnapshotAndReleaseError = "snapshot and release repositories must be set. "
    62  )
    63  
    64  type ConfigFile struct {
    65  	Interactive bool               `yaml:"-"`
    66  	Version     int                `yaml:"version,omitempty"`
    67  	ConfigType  string             `yaml:"type,omitempty"`
    68  	Resolver    project.Repository `yaml:"resolver,omitempty"`
    69  	Deployer    project.Repository `yaml:"deployer,omitempty"`
    70  	UsePlugin   bool               `yaml:"usePlugin,omitempty"`
    71  	UseWrapper  bool               `yaml:"useWrapper,omitempty"`
    72  }
    73  
    74  type ConfigOption func(c *ConfigFile)
    75  
    76  func newConfigFile(confType project.ProjectType) *ConfigFile {
    77  	return &ConfigFile{
    78  		Version:    BuildConfVersion,
    79  		ConfigType: confType.String(),
    80  	}
    81  }
    82  
    83  func NewConfigFileWithOptions(confType project.ProjectType, options ...ConfigOption) *ConfigFile {
    84  	configFile := newConfigFile(confType)
    85  	configFile.setDefaultValues(confType)
    86  
    87  	for _, option := range options {
    88  		option(configFile)
    89  	}
    90  	return configFile
    91  }
    92  
    93  func (configFile *ConfigFile) setDefaultValues(confType project.ProjectType) {
    94  	configFile.Interactive = !isCI()
    95  
    96  	if confType == project.Gradle {
    97  		configFile.Deployer.DeployMavenDesc = true
    98  		configFile.Deployer.DeployIvyDesc = true
    99  		configFile.Deployer.IvyPattern = defaultIvyDescPattern
   100  		configFile.Deployer.ArtifactsPattern = defaultIvyArtifactsPattern
   101  	}
   102  }
   103  
   104  func NewConfigFile(confType project.ProjectType, c *cli.Context) *ConfigFile {
   105  	configFile := newConfigFile(confType)
   106  	configFile.populateConfigFromFlags(c)
   107  	switch confType {
   108  	case project.Maven:
   109  		configFile.populateMavenConfigFromFlags(c)
   110  	case project.Gradle:
   111  		configFile.populateGradleConfigFromFlags(c)
   112  	case project.Nuget, project.Dotnet:
   113  		configFile.populateNugetConfigFromFlags(c)
   114  	}
   115  	return configFile
   116  }
   117  
   118  func CreateBuildConfig(c *cli.Context, confType project.ProjectType) (err error) {
   119  	return createBuildConfig(c.Bool(global), confType, NewConfigFile(confType, c))
   120  }
   121  
   122  func CreateBuildConfigWithOptions(global bool, confType project.ProjectType, options ...ConfigOption) (err error) {
   123  	return createBuildConfig(global, confType, NewConfigFileWithOptions(confType, options...))
   124  }
   125  
   126  func createBuildConfig(global bool, confType project.ProjectType, configFile *ConfigFile) (err error) {
   127  	// Create/verify project directory
   128  	projectDir, err := utils.GetProjectDir(global)
   129  	if err != nil {
   130  		return err
   131  	}
   132  	if err = fileutils.CreateDirIfNotExist(projectDir); err != nil {
   133  		return err
   134  	}
   135  	// Populate, validate and write the config file
   136  	configFilePath := filepath.Join(projectDir, confType.String()+".yaml")
   137  	if err := configFile.VerifyConfigFile(configFilePath); err != nil {
   138  		return err
   139  	}
   140  	if err := handleInteractiveConfigCreation(configFile, confType); err != nil {
   141  		return err
   142  	}
   143  	if err = configFile.validateConfig(); err != nil {
   144  		return err
   145  	}
   146  	return writeConfigFile(configFile, configFilePath)
   147  }
   148  
   149  func handleInteractiveConfigCreation(configFile *ConfigFile, confType project.ProjectType) (err error) {
   150  	if !configFile.Interactive {
   151  		return
   152  	}
   153  	switch confType {
   154  	case project.Go:
   155  		return configFile.setDeployerResolver()
   156  	case project.Pip, project.Pipenv, project.Poetry:
   157  		return configFile.setResolver(false)
   158  	case project.Yarn:
   159  		return configFile.setResolver(false)
   160  	case project.Npm:
   161  		return configFile.setDeployerResolver()
   162  	case project.Nuget, project.Dotnet:
   163  		return configFile.configDotnet()
   164  	case project.Maven:
   165  		return configFile.configMaven()
   166  	case project.Gradle:
   167  		return configFile.configGradle()
   168  	case project.Terraform:
   169  		return configFile.setResolver(false)
   170  	}
   171  	return
   172  }
   173  
   174  func writeConfigFile(configFile *ConfigFile, destination string) (err error) {
   175  	resBytes, err := yaml.Marshal(&configFile)
   176  	if err != nil {
   177  		return errorutils.CheckError(err)
   178  	}
   179  	if err = os.WriteFile(destination, resBytes, 0644); err != nil {
   180  		return errorutils.CheckError(err)
   181  	}
   182  	log.Info(configFile.ConfigType + " build config successfully created.")
   183  	return
   184  }
   185  
   186  func isCI() bool {
   187  	return strings.ToLower(os.Getenv(coreutils.CI)) == "true"
   188  }
   189  
   190  func isInteractive(c *cli.Context) bool {
   191  	if isCI() {
   192  		return false
   193  	}
   194  	return !isAnyFlagSet(c, resolutionServerId, resolutionRepo, deploymentServerId, deploymentRepo)
   195  }
   196  
   197  func isAnyFlagSet(c *cli.Context, flagNames ...string) bool {
   198  	for _, flagName := range flagNames {
   199  		if c.IsSet(flagName) {
   200  			return true
   201  		}
   202  	}
   203  	return false
   204  }
   205  
   206  // Populate configuration from cli flags
   207  func (configFile *ConfigFile) populateConfigFromFlags(c *cli.Context) {
   208  	configFile.Resolver.ServerId = c.String(resolutionServerId)
   209  	configFile.Resolver.Repo = c.String(resolutionRepo)
   210  	configFile.Deployer.ServerId = c.String(deploymentServerId)
   211  	configFile.Deployer.Repo = c.String(deploymentRepo)
   212  	configFile.Interactive = isInteractive(c)
   213  }
   214  
   215  func WithResolverServerId(serverId string) ConfigOption {
   216  	return func(c *ConfigFile) {
   217  		c.Resolver.ServerId = serverId
   218  		c.Interactive = false
   219  	}
   220  }
   221  
   222  func WithDeployerServerId(serverId string) ConfigOption {
   223  	return func(c *ConfigFile) {
   224  		c.Deployer.ServerId = serverId
   225  		c.Interactive = false
   226  	}
   227  }
   228  
   229  func WithResolverRepo(repoId string) ConfigOption {
   230  	return func(c *ConfigFile) {
   231  		c.Resolver.Repo = repoId
   232  		c.Interactive = false
   233  	}
   234  }
   235  
   236  func WithDeployerRepo(repoId string) ConfigOption {
   237  	return func(c *ConfigFile) {
   238  		c.Deployer.Repo = repoId
   239  		c.Interactive = false
   240  	}
   241  }
   242  
   243  // Populate Maven related configuration from cli flags
   244  func (configFile *ConfigFile) populateMavenConfigFromFlags(c *cli.Context) {
   245  	configFile.Resolver.SnapshotRepo = c.String(resolutionSnapshotsRepo)
   246  	configFile.Resolver.ReleaseRepo = c.String(resolutionReleasesRepo)
   247  	configFile.Deployer.SnapshotRepo = c.String(deploymentSnapshotsRepo)
   248  	configFile.Deployer.ReleaseRepo = c.String(deploymentReleasesRepo)
   249  	configFile.Deployer.IncludePatterns = c.String(includePatterns)
   250  	configFile.Deployer.ExcludePatterns = c.String(excludePatterns)
   251  	configFile.UseWrapper = c.Bool(useWrapper)
   252  	configFile.Interactive = configFile.Interactive && !isAnyFlagSet(c, resolutionSnapshotsRepo, resolutionReleasesRepo,
   253  		deploymentSnapshotsRepo, deploymentReleasesRepo, includePatterns, excludePatterns)
   254  }
   255  
   256  func WithResolverSnapshotRepo(repoId string) ConfigOption {
   257  	return func(c *ConfigFile) {
   258  		c.Resolver.SnapshotRepo = repoId
   259  		c.Interactive = false
   260  	}
   261  }
   262  
   263  func WithResolverReleaseRepo(repoId string) ConfigOption {
   264  	return func(c *ConfigFile) {
   265  		c.Resolver.ReleaseRepo = repoId
   266  		c.Interactive = false
   267  	}
   268  }
   269  
   270  func WithDeployerSnapshotRepo(repoId string) ConfigOption {
   271  	return func(c *ConfigFile) {
   272  		c.Deployer.SnapshotRepo = repoId
   273  		c.Interactive = false
   274  	}
   275  }
   276  
   277  func WithDeployerReleaseRepo(repoId string) ConfigOption {
   278  	return func(c *ConfigFile) {
   279  		c.Deployer.ReleaseRepo = repoId
   280  		c.Interactive = false
   281  	}
   282  }
   283  
   284  func WithDeployerIncludePatterns(includePatterns string) ConfigOption {
   285  	return func(c *ConfigFile) {
   286  		c.Deployer.IncludePatterns = includePatterns
   287  		c.Interactive = false
   288  	}
   289  }
   290  
   291  func WithDeployerExcludePatterns(excludePatterns string) ConfigOption {
   292  	return func(c *ConfigFile) {
   293  		c.Deployer.ExcludePatterns = excludePatterns
   294  		c.Interactive = false
   295  	}
   296  }
   297  
   298  func UseWrapper(useWrapper bool) ConfigOption {
   299  	return func(c *ConfigFile) {
   300  		c.UseWrapper = useWrapper
   301  	}
   302  }
   303  
   304  // Populate Gradle related configuration from cli flags
   305  func (configFile *ConfigFile) populateGradleConfigFromFlags(c *cli.Context) {
   306  	configFile.Deployer.DeployMavenDesc = c.BoolT(deployMavenDesc)
   307  	configFile.Deployer.DeployIvyDesc = c.BoolT(deployIvyDesc)
   308  	configFile.Deployer.IvyPattern = defaultIfNotSet(c, ivyDescPattern, defaultIvyDescPattern)
   309  	configFile.Deployer.ArtifactsPattern = defaultIfNotSet(c, ivyArtifactsPattern, defaultIvyArtifactsPattern)
   310  	configFile.UsePlugin = c.Bool(usesPlugin)
   311  	configFile.UseWrapper = c.Bool(useWrapper)
   312  	configFile.Interactive = configFile.Interactive && !isAnyFlagSet(c, deployMavenDesc, deployIvyDesc, ivyDescPattern, ivyArtifactsPattern, usesPlugin, useWrapper)
   313  }
   314  
   315  func WithMavenDescDeployment(mavenDesc bool) ConfigOption {
   316  	return func(c *ConfigFile) {
   317  		c.Deployer.DeployMavenDesc = mavenDesc
   318  		c.Interactive = false
   319  	}
   320  }
   321  
   322  func WithIvyDescDeployment(ivyDesc bool) ConfigOption {
   323  	return func(c *ConfigFile) {
   324  		c.Deployer.DeployIvyDesc = ivyDesc
   325  		c.Interactive = false
   326  	}
   327  }
   328  
   329  func WithIvyDeploymentPattern(ivyPattern string) ConfigOption {
   330  	return func(c *ConfigFile) {
   331  		c.Deployer.IvyPattern = ivyPattern
   332  		c.Interactive = false
   333  	}
   334  }
   335  
   336  func WithArtifactsDeploymentPattern(artifactsPattern string) ConfigOption {
   337  	return func(c *ConfigFile) {
   338  		c.Deployer.IvyPattern = artifactsPattern
   339  		c.Interactive = false
   340  	}
   341  }
   342  
   343  func UsePlugin(usePlugin bool) ConfigOption {
   344  	return func(c *ConfigFile) {
   345  		c.UsePlugin = usePlugin
   346  		c.Interactive = false
   347  	}
   348  }
   349  
   350  // Populate NuGet related configuration from cli flags
   351  func (configFile *ConfigFile) populateNugetConfigFromFlags(c *cli.Context) {
   352  	configFile.Resolver.NugetV2 = c.Bool(nugetV2)
   353  	configFile.Interactive = configFile.Interactive && !isAnyFlagSet(c, nugetV2)
   354  }
   355  
   356  func WithResolverNugetV2(nugetV2 bool) ConfigOption {
   357  	return func(c *ConfigFile) {
   358  		c.Resolver.NugetV2 = nugetV2
   359  		c.Interactive = false
   360  	}
   361  }
   362  
   363  // Verify config file doesn't exist or prompt to override it
   364  func (configFile *ConfigFile) VerifyConfigFile(configFilePath string) error {
   365  	exists, err := fileutils.IsFileExists(configFilePath, false)
   366  	if err != nil {
   367  		return err
   368  	}
   369  	if exists {
   370  		if !configFile.Interactive {
   371  			return nil
   372  		}
   373  		override := coreutils.AskYesNo("Configuration file already exists at "+configFilePath+". Override it?", false)
   374  		if !override {
   375  			return errorutils.CheckErrorf("operation canceled")
   376  		}
   377  		return nil
   378  	}
   379  
   380  	// Create config file to make sure the path is valid
   381  	f, err := os.OpenFile(configFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
   382  	if errorutils.CheckError(err) != nil {
   383  		return err
   384  	}
   385  	err = f.Close()
   386  	if errorutils.CheckError(err) != nil {
   387  		return err
   388  	}
   389  	// The file will be written at the end of successful configuration command.
   390  	return errorutils.CheckError(os.Remove(configFilePath))
   391  }
   392  
   393  func (configFile *ConfigFile) configDotnet() error {
   394  	if err := configFile.setResolver(false); err != nil {
   395  		return err
   396  	}
   397  	if configFile.Resolver.ServerId != "" {
   398  		configFile.setUseNugetV2()
   399  	}
   400  	return nil
   401  }
   402  
   403  func (configFile *ConfigFile) configMaven() error {
   404  	if err := configFile.setDeployerResolverWithSnapshot(); err != nil {
   405  		return err
   406  	}
   407  	if configFile.Deployer.ServerId != "" {
   408  		configFile.setIncludeExcludePatterns()
   409  	}
   410  	configFile.UseWrapper = coreutils.AskYesNo("Use Maven wrapper?", true)
   411  	return nil
   412  }
   413  
   414  func (configFile *ConfigFile) setIncludeExcludePatterns() {
   415  	if !coreutils.AskYesNo("Would you like to filter out some of the deployed artifacts?", false) {
   416  		return
   417  	}
   418  	log.Output("You may set multiple wildcard patterns, to match the artifacts' names you'd like to include and/or exclude from being deployed.")
   419  	includePatterns := getIncludeExcludePatterns("include")
   420  	if includePatterns != "" {
   421  		configFile.Deployer.IncludePatterns = includePatterns
   422  	}
   423  	excludePatterns := getIncludeExcludePatterns("exclude")
   424  	if excludePatterns != "" {
   425  		configFile.Deployer.ExcludePatterns = excludePatterns
   426  	}
   427  }
   428  
   429  func getIncludeExcludePatterns(patternType string) string {
   430  	var patterns []string
   431  	patternNum := 1
   432  	for {
   433  		newPattern := ioutils.AskString("", cases.Title(language.Und, cases.NoLower).String(patternType)+" pattern "+strconv.Itoa(patternNum)+" (leave empty to continue):", true, false)
   434  		if newPattern == "" {
   435  			return strings.Join(patterns, ", ")
   436  		}
   437  		patterns = append(patterns, newPattern)
   438  		patternNum++
   439  	}
   440  }
   441  
   442  func (configFile *ConfigFile) configGradle() error {
   443  	if err := configFile.setDeployerResolver(); err != nil {
   444  		return err
   445  	}
   446  	if configFile.Deployer.ServerId != "" {
   447  		configFile.setMavenIvyDescriptors()
   448  	}
   449  	configFile.readGradleGlobalConfig()
   450  	return nil
   451  }
   452  
   453  func (configFile *ConfigFile) readGradleGlobalConfig() {
   454  	configFile.UsePlugin = coreutils.AskYesNo("Is the Gradle Artifactory Plugin already applied in the build script?", false)
   455  	configFile.UseWrapper = coreutils.AskYesNo("Use Gradle wrapper?", true)
   456  }
   457  
   458  func (configFile *ConfigFile) setDeployer(withSnapshot bool) error {
   459  	// Set deployer id
   460  	if err := configFile.setDeployerId(); err != nil {
   461  		return err
   462  	}
   463  
   464  	// Set deployment repository
   465  	if configFile.Deployer.ServerId != "" {
   466  		deployerRepos, err := getRepositories(configFile.Deployer.ServerId, utils.Virtual, utils.Local)
   467  		if err != nil {
   468  			log.Error("failed getting repositories list: " + err.Error())
   469  			// Continue without auto complete.
   470  			deployerRepos = []string{}
   471  		}
   472  		if withSnapshot {
   473  			configFile.setRepo(&configFile.Deployer.ReleaseRepo, "Set repository for release artifacts deployment", deployerRepos)
   474  			configFile.setRepo(&configFile.Deployer.SnapshotRepo, "Set repository for snapshot artifacts deployment", deployerRepos)
   475  		} else {
   476  			configFile.setRepo(&configFile.Deployer.Repo, "Set repository for artifacts deployment", deployerRepos)
   477  		}
   478  	}
   479  	return nil
   480  }
   481  
   482  func (configFile *ConfigFile) setResolver(withSnapshot bool) error {
   483  	// Set resolver id
   484  	if err := configFile.setResolverId(); err != nil {
   485  		return err
   486  	}
   487  	// Set resolution repository
   488  	if configFile.Resolver.ServerId != "" {
   489  		resolverRepos, err := getRepositories(configFile.Resolver.ServerId, utils.Virtual, utils.Remote)
   490  		if err != nil {
   491  			log.Error("failed getting repositories list: " + err.Error())
   492  			// Continue without auto complete.
   493  			resolverRepos = []string{}
   494  		}
   495  		if withSnapshot {
   496  			configFile.setRepo(&configFile.Resolver.ReleaseRepo, "Set resolution repository for release dependencies", resolverRepos)
   497  			configFile.setRepo(&configFile.Resolver.SnapshotRepo, "Set resolution repository for snapshot dependencies", resolverRepos)
   498  		} else {
   499  			configFile.setRepo(&configFile.Resolver.Repo, "Set repository for dependencies resolution", resolverRepos)
   500  		}
   501  	}
   502  	return nil
   503  }
   504  
   505  func (configFile *ConfigFile) setDeployerResolver() error {
   506  	if err := configFile.setResolver(false); err != nil {
   507  		return err
   508  	}
   509  	return configFile.setDeployer(false)
   510  }
   511  
   512  func (configFile *ConfigFile) setDeployerResolverWithSnapshot() error {
   513  	if err := configFile.setResolver(true); err != nil {
   514  		return err
   515  	}
   516  	return configFile.setDeployer(true)
   517  }
   518  
   519  func (configFile *ConfigFile) setResolverId() error {
   520  	return configFile.setServerId(&configFile.Resolver.ServerId, "Resolve dependencies from Artifactory?")
   521  }
   522  
   523  func (configFile *ConfigFile) setDeployerId() error {
   524  	return configFile.setServerId(&configFile.Deployer.ServerId, "Deploy project artifacts to Artifactory?")
   525  }
   526  
   527  func (configFile *ConfigFile) setServerId(serverId *string, useArtifactoryQuestion string) error {
   528  	var err error
   529  	*serverId, err = readArtifactoryServer(useArtifactoryQuestion)
   530  	return err
   531  }
   532  
   533  func (configFile *ConfigFile) setRepo(repo *string, promptPrefix string, availableRepos []string) {
   534  	if *repo == "" {
   535  		if len(availableRepos) > 0 {
   536  			*repo = ioutils.AskFromListWithMismatchConfirmation(promptPrefix, "Repository not found.", ioutils.ConvertToSuggests(availableRepos))
   537  		} else {
   538  			*repo = ioutils.AskString("", promptPrefix, false, false)
   539  		}
   540  	}
   541  }
   542  
   543  func (configFile *ConfigFile) setMavenIvyDescriptors() {
   544  	configFile.Deployer.DeployMavenDesc = coreutils.AskYesNo("Deploy Maven descriptors?", false)
   545  	configFile.Deployer.DeployIvyDesc = coreutils.AskYesNo("Deploy Ivy descriptors?", false)
   546  
   547  	if configFile.Deployer.DeployIvyDesc {
   548  		configFile.Deployer.IvyPattern = ioutils.AskStringWithDefault("", "Set Ivy descriptor pattern", defaultIvyDescPattern)
   549  		configFile.Deployer.ArtifactsPattern = ioutils.AskStringWithDefault("", "Set Ivy artifact pattern", defaultIvyArtifactsPattern)
   550  	}
   551  }
   552  
   553  func (configFile *ConfigFile) setUseNugetV2() {
   554  	configFile.Resolver.NugetV2 = coreutils.AskYesNo("Use NuGet V2 Protocol?", false)
   555  }
   556  
   557  func validateRepositoryConfig(repository *project.Repository, errorPrefix string) error {
   558  	releaseRepo := repository.ReleaseRepo
   559  	snapshotRepo := repository.SnapshotRepo
   560  
   561  	if repository.ServerId != "" && repository.Repo == "" && releaseRepo == "" && snapshotRepo == "" {
   562  		return errorutils.CheckErrorf(errorPrefix + setRepositoryError)
   563  	}
   564  	// Server-id flag was not provided.
   565  	if repository.ServerId == "" {
   566  		// If no Server ID provided, check if provided via environment variable
   567  		serverId := os.Getenv(coreutils.ServerID)
   568  		if serverId == "" {
   569  			// For config commands - resolver/deployer server-id flags are optional.
   570  			// In case no server-id flag was provided we use the default configured server id.
   571  			defaultServerDetails, err := config.GetDefaultServerConf()
   572  			if err != nil {
   573  				return err
   574  			}
   575  			if defaultServerDetails != nil {
   576  				serverId = defaultServerDetails.ServerId
   577  			}
   578  		}
   579  		// No default server was configured and also no environment variable
   580  		if serverId == "" {
   581  			// Repositories flags were provided.
   582  			if repository.Repo != "" || releaseRepo != "" || snapshotRepo != "" {
   583  				return errorutils.CheckErrorf(errorPrefix + setServerIdError)
   584  			}
   585  		} else if repository.Repo != "" || releaseRepo != "" || snapshotRepo != "" {
   586  			// Server-id flag wasn't provided and repositories flags were provided - the default configured global server will be chosen.
   587  			repository.ServerId = serverId
   588  		}
   589  	}
   590  	// Release/snapshot repositories should be entangled to each other.
   591  	if (releaseRepo == "" && snapshotRepo != "") || (releaseRepo != "" && snapshotRepo == "") {
   592  		return errorutils.CheckErrorf(errorPrefix + setSnapshotAndReleaseError)
   593  	}
   594  	return nil
   595  }
   596  
   597  // Validate spec file configuration
   598  func (configFile *ConfigFile) validateConfig() error {
   599  	err := validateRepositoryConfig(&configFile.Resolver, resolutionErrorPrefix)
   600  	if err != nil {
   601  		return err
   602  	}
   603  	return validateRepositoryConfig(&configFile.Deployer, deploymentErrorPrefix)
   604  }
   605  
   606  // Get Artifactory serverId from the user. If useArtifactoryQuestion is not empty, ask first whether to use artifactory.
   607  func readArtifactoryServer(useArtifactoryQuestion string) (string, error) {
   608  	// Get all Artifactory servers
   609  	serversIds, defaultServer, err := getServersIdAndDefault()
   610  	if err != nil {
   611  		return "", err
   612  	}
   613  	if len(serversIds) == 0 {
   614  		return "", errorutils.CheckErrorf("No Artifactory servers configured. Use the 'jfrog c add' command to set the Artifactory server details.")
   615  	}
   616  
   617  	// Ask whether to use artifactory
   618  	if useArtifactoryQuestion != "" {
   619  		useArtifactory := coreutils.AskYesNo(useArtifactoryQuestion, true)
   620  		if !useArtifactory {
   621  			return "", nil
   622  		}
   623  	}
   624  
   625  	return ioutils.AskFromList("", "Set Artifactory server ID", false, ioutils.ConvertToSuggests(serversIds), defaultServer), nil
   626  }
   627  
   628  func getServersIdAndDefault() ([]string, string, error) {
   629  	allConfigs, err := config.GetAllServersConfigs()
   630  	if err != nil {
   631  		return nil, "", err
   632  	}
   633  	var defaultVal string
   634  	var serversId []string
   635  	for _, v := range allConfigs {
   636  		if v.IsDefault {
   637  			defaultVal = v.ServerId
   638  		}
   639  		serversId = append(serversId, v.ServerId)
   640  	}
   641  	return serversId, defaultVal, nil
   642  }
   643  
   644  func getRepositories(serverId string, repoTypes ...utils.RepoType) ([]string, error) {
   645  	artDetails, err := config.GetSpecificConfig(serverId, false, true)
   646  	if err != nil {
   647  		return nil, err
   648  	}
   649  
   650  	return utils.GetRepositories(artDetails, repoTypes...)
   651  }
   652  
   653  func defaultIfNotSet(c *cli.Context, flagName string, defaultValue string) string {
   654  	if c.IsSet(flagName) {
   655  		return c.String(flagName)
   656  	}
   657  	return defaultValue
   658  }