github.com/osievert/jfrog-cli-core@v1.2.7/artifactory/utils/buildinfoproperties.go (about) 1 package utils 2 3 import ( 4 "errors" 5 "github.com/jfrog/jfrog-cli-core/utils/ioutils" 6 "io/ioutil" 7 "net" 8 "net/url" 9 "os" 10 "path/filepath" 11 "strconv" 12 "time" 13 14 "github.com/jfrog/jfrog-cli-core/utils/config" 15 "github.com/jfrog/jfrog-cli-core/utils/coreutils" 16 17 "github.com/jfrog/jfrog-client-go/auth" 18 "github.com/jfrog/jfrog-client-go/utils/errorutils" 19 "github.com/spf13/viper" 20 ) 21 22 const ( 23 HttpProxy = "HTTP_PROXY" 24 ) 25 26 type BuildConfigMapping map[ProjectType][]*map[string]string 27 28 var buildTypeConfigMapping = BuildConfigMapping{ 29 Maven: {&commonConfigMapping, &mavenConfigMapping}, 30 Gradle: {&commonConfigMapping, &gradleConfigMapping}, 31 } 32 33 type ConfigType string 34 35 const ( 36 YAML ConfigType = "yaml" 37 PROPERTIES ConfigType = "properties" 38 ) 39 40 // For key/value binding 41 const BUILD_NAME = "build.name" 42 const BUILD_NUMBER = "build.number" 43 const BUILD_TIMESTAMP = "build.timestamp" 44 const GENERATED_BUILD_INFO = "buildInfo.generated" 45 const INSECURE_TLS = "insecureTls" 46 47 const RESOLVER_PREFIX = "resolver." 48 const DEPLOYER_PREFIX = "deployer." 49 50 const REPO = "repo" 51 const SNAPSHOT_REPO = "snapshotRepo" 52 const RELEASE_REPO = "releaseRepo" 53 54 const SERVER_ID = "serverId" 55 const URL = "url" 56 const USERNAME = "username" 57 const PASSWORD = "password" 58 const DEPLOY_ARTIFACTS = "artifacts" 59 60 const MAVEN_DESCRIPTOR = "deployMavenDescriptors" 61 const IVY_DESCRIPTOR = "deployIvyDescriptors" 62 const IVY_PATTERN = "ivyPattern" 63 const ARTIFACT_PATTERN = "artifactPattern" 64 const USE_GRADLE_PLUGIN = "usePlugin" 65 const USE_GRADLE_WRAPPER = "useWrapper" 66 const FORK_COUNT = "forkCount" 67 68 // For path and temp files 69 const PROPERTIES_TEMP_PREFIX = "buildInfoProperties" 70 const PROPERTIES_TEMP_PATH = "jfrog/properties/" 71 const GENERATED_BUILD_INFO_TEMP_PREFIX = "generatedBuildInfo" 72 73 const PROXY = "proxy." 74 const HOST = "host" 75 const PORT = "port" 76 77 // Config mapping are used to create buildInfo properties file to be used by BuildInfo extractors. 78 // Build config provided by the user may contain other properties that will not be included in the properties file. 79 var defaultPropertiesValues = map[string]string{ 80 "artifactory.publish.artifacts": "true", 81 "artifactory.publish.buildInfo": "false", 82 "artifactory.publish.unstable": "false", 83 "artifactory.publish.maven": "false", 84 "artifactory.publish.ivy": "false", 85 "buildInfoConfig.includeEnvVars": "false", 86 "buildInfoConfig.envVarsExcludePatterns": "*password*,*psw*,*secret*,*key*,*token*", 87 "buildInfo.agent.name": coreutils.GetClientAgent() + "/" + coreutils.GetVersion(), 88 "buildInfo.licenseControl.autoDiscover": "true", 89 "buildInfo.licenseControl.includePublishedArtifacts": "false", 90 "buildInfo.licenseControl.runChecks": "false", 91 "org.jfrog.build.extractor.maven.recorder.activate": "true", 92 "buildInfo.env.extractor.used": "true", 93 "artifactory.publish.forkCount": "3", 94 } 95 96 var commonConfigMapping = map[string]string{ 97 "artifactory.publish.buildInfo": "", 98 "artifactory.publish.unstable": "", 99 "buildInfoConfig.includeEnvVars": "", 100 "buildInfoConfig.envVarsExcludePatterns": "", 101 "buildInfo.agent.name": "", 102 "buildInfo.licenseControl.autoDiscover": "", 103 "buildInfo.licenseControl.includePublishedArtifacts": "", 104 "buildInfo.licenseControl.runChecks": "", 105 "artifactory.resolve.contextUrl": RESOLVER_PREFIX + URL, 106 "artifactory.resolve.username": RESOLVER_PREFIX + USERNAME, 107 "artifactory.resolve.password": RESOLVER_PREFIX + PASSWORD, 108 "artifactory.publish.contextUrl": DEPLOYER_PREFIX + URL, 109 "artifactory.publish.username": DEPLOYER_PREFIX + USERNAME, 110 "artifactory.publish.password": DEPLOYER_PREFIX + PASSWORD, 111 "artifactory.publish.artifacts": DEPLOYER_PREFIX + DEPLOY_ARTIFACTS, 112 "artifactory.deploy.build.name": BUILD_NAME, 113 "artifactory.deploy.build.number": BUILD_NUMBER, 114 "artifactory.deploy.build.timestamp": BUILD_TIMESTAMP, 115 "buildInfo.generated.build.info": GENERATED_BUILD_INFO, 116 "artifactory.proxy.host": PROXY + HOST, 117 "artifactory.proxy.port": PROXY + PORT, 118 "artifactory.publish.forkCount": FORK_COUNT, 119 "artifactory.insecureTls": INSECURE_TLS, 120 } 121 122 var mavenConfigMapping = map[string]string{ 123 "org.jfrog.build.extractor.maven.recorder.activate": "", 124 "artifactory.resolve.repoKey": RESOLVER_PREFIX + RELEASE_REPO, 125 "artifactory.resolve.downSnapshotRepoKey": RESOLVER_PREFIX + SNAPSHOT_REPO, 126 "artifactory.publish.repoKey": DEPLOYER_PREFIX + RELEASE_REPO, 127 "artifactory.publish.snapshot.repoKey": DEPLOYER_PREFIX + SNAPSHOT_REPO, 128 } 129 130 var gradleConfigMapping = map[string]string{ 131 "buildInfo.env.extractor.used": "", 132 "org.jfrog.build.extractor.maven.recorder.activate": "", 133 "artifactory.resolve.repoKey": RESOLVER_PREFIX + REPO, 134 "artifactory.resolve.downSnapshotRepoKey": RESOLVER_PREFIX + REPO, 135 "artifactory.publish.repoKey": DEPLOYER_PREFIX + REPO, 136 "artifactory.publish.snapshot.repoKey": DEPLOYER_PREFIX + REPO, 137 "artifactory.publish.maven": DEPLOYER_PREFIX + MAVEN_DESCRIPTOR, 138 "artifactory.publish.ivy": DEPLOYER_PREFIX + IVY_DESCRIPTOR, 139 "artifactory.publish.ivy.ivyPattern": DEPLOYER_PREFIX + IVY_PATTERN, 140 "artifactory.publish.ivy.artPattern": DEPLOYER_PREFIX + ARTIFACT_PATTERN, 141 "buildInfo.build.name": BUILD_NAME, 142 "buildInfo.build.number": BUILD_NUMBER, 143 } 144 145 func ReadConfigFile(configPath string, configType ConfigType) (*viper.Viper, error) { 146 config := viper.New() 147 config.SetConfigType(string(configType)) 148 149 f, err := os.Open(configPath) 150 if err != nil { 151 return config, errorutils.CheckError(err) 152 } 153 err = config.ReadConfig(f) 154 if err != nil { 155 return config, errorutils.CheckError(err) 156 } 157 158 return config, nil 159 } 160 161 // Returns the Artifactory details 162 // Checks first for the deployer information if exists and if not, checks for the resolver information. 163 func GetRtDetails(vConfig *viper.Viper) (*config.ArtifactoryDetails, error) { 164 if vConfig.IsSet(DEPLOYER_PREFIX + SERVER_ID) { 165 serverId := vConfig.GetString(DEPLOYER_PREFIX + SERVER_ID) 166 return config.GetArtifactorySpecificConfig(serverId, true, true) 167 } 168 169 if vConfig.IsSet(RESOLVER_PREFIX + SERVER_ID) { 170 serverId := vConfig.GetString(RESOLVER_PREFIX + SERVER_ID) 171 return config.GetArtifactorySpecificConfig(serverId, true, true) 172 } 173 return nil, nil 174 } 175 176 func CreateBuildInfoPropertiesFile(buildName, buildNumber string, config *viper.Viper, projectType ProjectType) (string, error) { 177 if config.GetString("type") != projectType.String() { 178 return "", errorutils.CheckError(errors.New("Incompatible build config, expected: " + projectType.String() + " got: " + config.GetString("type"))) 179 } 180 181 propertiesPath := filepath.Join(coreutils.GetCliPersistentTempDirPath(), PROPERTIES_TEMP_PATH) 182 err := os.MkdirAll(propertiesPath, 0777) 183 if errorutils.CheckError(err) != nil { 184 return "", err 185 } 186 propertiesFile, err := ioutil.TempFile(propertiesPath, PROPERTIES_TEMP_PREFIX) 187 defer propertiesFile.Close() 188 if err != nil { 189 return "", errorutils.CheckError(err) 190 } 191 err = setServerDetailsToConfig(RESOLVER_PREFIX, config) 192 if err != nil { 193 return "", err 194 } 195 err = setServerDetailsToConfig(DEPLOYER_PREFIX, config) 196 if err != nil { 197 return "", err 198 } 199 if buildName != "" || buildNumber != "" { 200 err = SaveBuildGeneralDetails(buildName, buildNumber) 201 if err != nil { 202 return "", err 203 } 204 err = setBuildTimestampToConfig(buildName, buildNumber, config) 205 if err != nil { 206 return "", err 207 } 208 err = createGeneratedBuildInfoFile(buildName, buildNumber, config) 209 if err != nil { 210 return "", err 211 } 212 } 213 err = setProxyIfDefined(config) 214 if err != nil { 215 return "", err 216 } 217 218 // Iterate over all the required properties keys according to the buildType and create properties file. 219 // If a value is provided by the build config file write it, 220 // otherwise use the default value from defaultPropertiesValues map. 221 for _, partialMapping := range buildTypeConfigMapping[projectType] { 222 for propertyKey, configKey := range *partialMapping { 223 if config.IsSet(configKey) { 224 _, err = propertiesFile.WriteString(propertyKey + "=" + config.GetString(configKey) + "\n") 225 } else if defaultVal, ok := defaultPropertiesValues[propertyKey]; ok { 226 _, err = propertiesFile.WriteString(propertyKey + "=" + defaultVal + "\n") 227 } 228 if err != nil { 229 return "", errorutils.CheckError(err) 230 } 231 } 232 } 233 return propertiesFile.Name(), nil 234 } 235 236 // If the HTTP_PROXY environment variable is set, add to the config proxy details. 237 func setProxyIfDefined(config *viper.Viper) error { 238 // Add HTTP_PROXY if exists 239 proxy := os.Getenv(HttpProxy) 240 if proxy != "" { 241 url, err := url.Parse(proxy) 242 if err != nil { 243 return errorutils.CheckError(err) 244 } 245 host, port, err := net.SplitHostPort(url.Host) 246 if err != nil { 247 return errorutils.CheckError(err) 248 } 249 config.Set(PROXY+HOST, host) 250 config.Set(PROXY+PORT, port) 251 } 252 return nil 253 } 254 255 func setServerDetailsToConfig(contextPrefix string, vConfig *viper.Viper) error { 256 if !vConfig.IsSet(contextPrefix + SERVER_ID) { 257 return nil 258 } 259 260 serverId := vConfig.GetString(contextPrefix + SERVER_ID) 261 artDetails, err := config.GetArtifactorySpecificConfig(serverId, true, true) 262 if err != nil { 263 return err 264 } 265 if artDetails.GetUrl() == "" { 266 return errorutils.CheckError(errors.New("Server ID " + serverId + ": URL is required.")) 267 } 268 vConfig.Set(contextPrefix+URL, artDetails.GetUrl()) 269 270 if artDetails.GetApiKey() != "" { 271 return errorutils.CheckError(errors.New("Server ID " + serverId + ": Configuring an API key without a username is not supported.")) 272 } 273 274 if artDetails.GetAccessToken() != "" { 275 username, err := auth.ExtractUsernameFromAccessToken(artDetails.GetAccessToken()) 276 if err != nil { 277 return err 278 } 279 vConfig.Set(contextPrefix+USERNAME, username) 280 vConfig.Set(contextPrefix+PASSWORD, artDetails.GetAccessToken()) 281 return nil 282 } 283 284 if artDetails.GetUser() != "" && artDetails.GetPassword() != "" { 285 vConfig.Set(contextPrefix+USERNAME, artDetails.GetUser()) 286 vConfig.Set(contextPrefix+PASSWORD, artDetails.GetPassword()) 287 } 288 return nil 289 } 290 291 // Generated build info file is template file where build-info will be written to during the 292 // Maven or Gradle build. 293 // Creating this file only if build name and number is provided. 294 func createGeneratedBuildInfoFile(buildName, buildNumber string, config *viper.Viper) error { 295 config.Set(BUILD_NAME, buildName) 296 config.Set(BUILD_NUMBER, buildNumber) 297 298 buildPath, err := GetBuildDir(config.GetString(BUILD_NAME), config.GetString(BUILD_NUMBER)) 299 if err != nil { 300 return err 301 } 302 var tempFile *os.File 303 tempFile, err = ioutil.TempFile(buildPath, GENERATED_BUILD_INFO_TEMP_PREFIX) 304 defer tempFile.Close() 305 if err != nil { 306 return err 307 } 308 // If this is a Windows machine there is a need to modify the path for the build info file to match Java syntax with double \\ 309 path := ioutils.DoubleWinPathSeparator(tempFile.Name()) 310 config.Set(GENERATED_BUILD_INFO, path) 311 return nil 312 } 313 314 func setBuildTimestampToConfig(buildName, buildNumber string, config *viper.Viper) error { 315 buildGeneralDetails, err := ReadBuildInfoGeneralDetails(buildName, buildNumber) 316 if err != nil { 317 return err 318 } 319 config.Set(BUILD_TIMESTAMP, strconv.FormatInt(buildGeneralDetails.Timestamp.UnixNano()/int64(time.Millisecond), 10)) 320 return nil 321 }