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  }