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