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 }