github.com/jfrog/jfrog-cli-go@v1.22.1-0.20200318093948-4826ef344ffd/artifactory/commands/nuget/consume.go (about)

     1  package nuget
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"net/url"
     7  	"os"
     8  	"path"
     9  	"strings"
    10  
    11  	gofrogcmd "github.com/jfrog/gofrog/io"
    12  	"github.com/jfrog/jfrog-cli-go/artifactory/utils"
    13  	"github.com/jfrog/jfrog-cli-go/artifactory/utils/nuget"
    14  	"github.com/jfrog/jfrog-cli-go/artifactory/utils/nuget/solution"
    15  	"github.com/jfrog/jfrog-cli-go/utils/config"
    16  	"github.com/jfrog/jfrog-client-go/auth"
    17  	clientutils "github.com/jfrog/jfrog-client-go/utils"
    18  	"github.com/jfrog/jfrog-client-go/utils/errorutils"
    19  	"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
    20  	"github.com/jfrog/jfrog-client-go/utils/log"
    21  	"github.com/mattn/go-shellwords"
    22  )
    23  
    24  type NugetCommandArgs struct {
    25  	args               string
    26  	flags              string
    27  	repoName           string
    28  	solutionPath       string
    29  	buildConfiguration *utils.BuildConfiguration
    30  	rtDetails          *config.ArtifactoryDetails
    31  }
    32  
    33  type NugetCommand struct {
    34  	configFilePath string
    35  	*NugetCommandArgs
    36  }
    37  
    38  func NewNugetCommand() *NugetCommand {
    39  	return &NugetCommand{"", &NugetCommandArgs{}}
    40  }
    41  
    42  func (nc *NugetCommand) SetConfigFilePath(configFilePath string) *NugetCommand {
    43  	nc.configFilePath = configFilePath
    44  	return nc
    45  }
    46  
    47  func (nca *NugetCommandArgs) SetRtDetails(rtDetails *config.ArtifactoryDetails) *NugetCommandArgs {
    48  	nca.rtDetails = rtDetails
    49  	return nca
    50  }
    51  
    52  func (nca *NugetCommandArgs) SetBuildConfiguration(buildConfiguration *utils.BuildConfiguration) *NugetCommandArgs {
    53  	nca.buildConfiguration = buildConfiguration
    54  	return nca
    55  }
    56  
    57  func (nca *NugetCommandArgs) SetSolutionPath(solutionPath string) *NugetCommandArgs {
    58  	nca.solutionPath = solutionPath
    59  	return nca
    60  }
    61  
    62  func (nca *NugetCommandArgs) SetRepoName(repoName string) *NugetCommandArgs {
    63  	nca.repoName = repoName
    64  	return nca
    65  }
    66  
    67  func (nca *NugetCommandArgs) SetFlags(flags string) *NugetCommandArgs {
    68  	nca.flags = flags
    69  	return nca
    70  }
    71  
    72  func (nca *NugetCommandArgs) SetArgs(args string) *NugetCommandArgs {
    73  	nca.args = args
    74  	return nca
    75  }
    76  
    77  func (nc *NugetCommand) Run() error {
    78  	// Read config file.
    79  	log.Debug("Preparing to read the config file", nc.configFilePath)
    80  	vConfig, err := utils.ReadConfigFile(nc.configFilePath, utils.YAML)
    81  	if err != nil {
    82  		return err
    83  	}
    84  	// Extract resolution params.
    85  	resolveParams, err := utils.GetRepoConfigByPrefix(nc.configFilePath, utils.ProjectConfigResolverPrefix, vConfig)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	filteredNugetArgs, buildConfiguration, err := utils.ExtractBuildDetailsFromArgs(strings.Split(nc.args, " "))
    90  	RtDetails, err := resolveParams.RtDetails()
    91  	if err != nil {
    92  		return err
    93  	}
    94  	nc.SetArgs(strings.Join(filteredNugetArgs, " ")).
    95  		SetRepoName(resolveParams.TargetRepo()).
    96  		SetBuildConfiguration(buildConfiguration).
    97  		SetRtDetails(RtDetails)
    98  	return nc.run()
    99  }
   100  
   101  // Exec all consume type nuget commands, install, update, add, restore.
   102  func (nca *NugetCommandArgs) run() error {
   103  	log.Info("Running nuget...")
   104  	// Use temp dir to save config file, the config will be removed at the end.
   105  	tempDirPath, err := fileutils.CreateTempDir()
   106  	if err != nil {
   107  		return err
   108  	}
   109  	defer fileutils.RemoveTempDir(tempDirPath)
   110  
   111  	nca.solutionPath, err = changeWorkingDir(nca.solutionPath)
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	err = nca.prepareAndRunCmd(tempDirPath)
   117  	if err != nil {
   118  		return err
   119  	}
   120  
   121  	isCollectBuildInfo := len(nca.buildConfiguration.BuildName) > 0 && len(nca.buildConfiguration.BuildNumber) > 0
   122  	if !isCollectBuildInfo {
   123  		return nil
   124  	}
   125  
   126  	slnFile := ""
   127  	flags := strings.Split(nca.flags, " ")
   128  	if len(flags) > 0 && strings.HasSuffix(flags[0], ".sln") {
   129  		slnFile = flags[0]
   130  	}
   131  	sol, err := solution.Load(nca.solutionPath, slnFile)
   132  	if err != nil {
   133  		return err
   134  	}
   135  
   136  	if err = utils.SaveBuildGeneralDetails(nca.buildConfiguration.BuildName, nca.buildConfiguration.BuildNumber); err != nil {
   137  		return err
   138  	}
   139  	buildInfo, err := sol.BuildInfo(nca.buildConfiguration.Module)
   140  	if err != nil {
   141  		return err
   142  	}
   143  	return utils.SaveBuildInfo(nca.buildConfiguration.BuildName, nca.buildConfiguration.BuildNumber, buildInfo)
   144  }
   145  
   146  func (nca *NugetCommandArgs) RtDetails() (*config.ArtifactoryDetails, error) {
   147  	return nca.rtDetails, nil
   148  }
   149  
   150  func (nca *NugetCommandArgs) CommandName() string {
   151  	return "rt_nuget"
   152  }
   153  
   154  const sourceName = "JFrogCli"
   155  
   156  func DependencyTreeCmd() error {
   157  	workspace, err := os.Getwd()
   158  	if err != nil {
   159  		return errorutils.CheckError(err)
   160  	}
   161  
   162  	sol, err := solution.Load(workspace, "")
   163  	if err != nil {
   164  		return err
   165  	}
   166  
   167  	// Create the tree for each project
   168  	for _, project := range sol.GetProjects() {
   169  		err = project.CreateDependencyTree()
   170  		if err != nil {
   171  			return err
   172  		}
   173  	}
   174  	// Build the tree.
   175  	content, err := sol.Marshal()
   176  	if err != nil {
   177  		return errorutils.CheckError(err)
   178  	}
   179  	log.Output(clientutils.IndentJson(content))
   180  	return nil
   181  }
   182  
   183  // Changes the working directory if provided.
   184  // Returns the path to the solution
   185  func changeWorkingDir(newWorkingDir string) (string, error) {
   186  	var err error
   187  	if newWorkingDir != "" {
   188  		err = os.Chdir(newWorkingDir)
   189  	} else {
   190  		newWorkingDir, err = os.Getwd()
   191  	}
   192  
   193  	return newWorkingDir, errorutils.CheckError(err)
   194  }
   195  
   196  // Prepares the nuget configuration file within the temp directory
   197  // Runs NuGet itself with the arguments and flags provided.
   198  func (nca *NugetCommandArgs) prepareAndRunCmd(configDirPath string) error {
   199  	cmd, err := nca.createNugetCmd()
   200  	if err != nil {
   201  		return err
   202  	}
   203  	// To prevent NuGet prompting for credentials
   204  	err = os.Setenv("NUGET_EXE_NO_PROMPT", "true")
   205  	if err != nil {
   206  		return errorutils.CheckError(err)
   207  	}
   208  
   209  	err = nca.prepareConfigFile(cmd, configDirPath)
   210  	if err != nil {
   211  		return err
   212  	}
   213  	err = gofrogcmd.RunCmd(cmd)
   214  	if err != nil {
   215  		return err
   216  	}
   217  
   218  	return nil
   219  }
   220  
   221  // Checks if the user provided input such as -configfile flag or -Source flag.
   222  // If those flags provided, NuGet will use the provided configs (default config file or the one with -configfile)
   223  // If neither provided, we are initializing our own config.
   224  func (nca *NugetCommandArgs) prepareConfigFile(cmd *nuget.Cmd, configDirPath string) error {
   225  	currentConfigPath, err := getFlagValueIfExists("-configfile", cmd)
   226  	if err != nil {
   227  		return err
   228  	}
   229  	if currentConfigPath != "" {
   230  		return nil
   231  	}
   232  
   233  	sourceCommandValue, err := getFlagValueIfExists("-source", cmd)
   234  	if err != nil {
   235  		return err
   236  	}
   237  	if sourceCommandValue != "" {
   238  		return nil
   239  	}
   240  
   241  	err = nca.initNewConfig(cmd, configDirPath)
   242  	return err
   243  }
   244  
   245  // Returns the value of the flag if exists
   246  func getFlagValueIfExists(cmdFlag string, cmd *nuget.Cmd) (string, error) {
   247  	for i := 0; i < len(cmd.CommandFlags); i++ {
   248  		if !strings.EqualFold(cmd.CommandFlags[i], cmdFlag) {
   249  			continue
   250  		}
   251  		if i+1 == len(cmd.CommandFlags) {
   252  			return "", errorutils.CheckError(errorutils.CheckError(fmt.Errorf(cmdFlag, " flag was provided without value")))
   253  		}
   254  		return cmd.CommandFlags[i+1], nil
   255  	}
   256  
   257  	return "", nil
   258  }
   259  
   260  // Initializing a new NuGet config file that NuGet will use into a temp file
   261  func (nca *NugetCommandArgs) initNewConfig(cmd *nuget.Cmd, configDirPath string) error {
   262  	// Got to here, means that neither of the flags provided and we need to init our own config.
   263  	configFile, err := writeToTempConfigFile(cmd, configDirPath)
   264  	if err != nil {
   265  		return err
   266  	}
   267  
   268  	return nca.addNugetAuthenticationToNewConfig(configFile)
   269  }
   270  
   271  // Runs nuget add sources and setapikey commands to authenticate with Artifactory server
   272  func (nca *NugetCommandArgs) addNugetAuthenticationToNewConfig(configFile *os.File) error {
   273  	sourceUrl, user, password, err := nca.getSourceDetails()
   274  	if err != nil {
   275  		return err
   276  	}
   277  
   278  	err = addNugetSource(configFile.Name(), sourceUrl, user, password)
   279  	if err != nil {
   280  		return err
   281  	}
   282  
   283  	err = addNugetApiKey(user, password, configFile.Name())
   284  	return err
   285  }
   286  
   287  // Creates the temp file and writes the config template into the file for NuGet can use it.
   288  func writeToTempConfigFile(cmd *nuget.Cmd, tempDirPath string) (*os.File, error) {
   289  	configFile, err := ioutil.TempFile(tempDirPath, "jfrog.cli.nuget.")
   290  	if err != nil {
   291  		return nil, errorutils.CheckError(err)
   292  	}
   293  	log.Debug("Nuget config file created at:", configFile.Name())
   294  
   295  	defer configFile.Close()
   296  
   297  	cmd.CommandFlags = append(cmd.CommandFlags, "-ConfigFile", configFile.Name())
   298  
   299  	// Set Artifactory repo as source
   300  	content := nuget.ConfigFileTemplate
   301  	_, err = configFile.WriteString(content)
   302  	if err != nil {
   303  		return nil, errorutils.CheckError(err)
   304  	}
   305  	return configFile, nil
   306  }
   307  
   308  // Runs nuget sources add command
   309  func addNugetSource(configFileName, sourceUrl, user, password string) error {
   310  	cmd, err := nuget.NewNugetCmd()
   311  	if err != nil {
   312  		return err
   313  	}
   314  
   315  	sourceCommand := "sources"
   316  	cmd.Command = append(cmd.Command, sourceCommand)
   317  	cmd.CommandFlags = append(cmd.CommandFlags, "-ConfigFile", configFileName)
   318  	cmd.CommandFlags = append(cmd.CommandFlags, "Add")
   319  	cmd.CommandFlags = append(cmd.CommandFlags, "-Name", sourceName)
   320  	cmd.CommandFlags = append(cmd.CommandFlags, "-Source", sourceUrl)
   321  	cmd.CommandFlags = append(cmd.CommandFlags, "-username", user)
   322  	cmd.CommandFlags = append(cmd.CommandFlags, "-password", password)
   323  	output, err := gofrogcmd.RunCmdOutput(cmd)
   324  	log.Debug("Running command: Add sources. Output:", output)
   325  	return err
   326  }
   327  
   328  // Runs nuget setapikey command
   329  func addNugetApiKey(user, password, configFileName string) error {
   330  	cmd, err := nuget.NewNugetCmd()
   331  	if err != nil {
   332  		return err
   333  	}
   334  
   335  	cmd.Command = append(cmd.Command, "setapikey")
   336  	cmd.CommandFlags = append(cmd.CommandFlags, user+":"+password)
   337  	cmd.CommandFlags = append(cmd.CommandFlags, "-Source", sourceName)
   338  	cmd.CommandFlags = append(cmd.CommandFlags, "-ConfigFile", configFileName)
   339  
   340  	output, err := gofrogcmd.RunCmdOutput(cmd)
   341  	log.Debug("Running command: SetApiKey. Output:", output)
   342  	return err
   343  }
   344  
   345  func (nca *NugetCommandArgs) getSourceDetails() (sourceURL, user, password string, err error) {
   346  	var u *url.URL
   347  	u, err = url.Parse(nca.rtDetails.Url)
   348  	if errorutils.CheckError(err) != nil {
   349  		return
   350  	}
   351  	u.Path = path.Join(u.Path, "api/nuget", nca.repoName)
   352  	sourceURL = u.String()
   353  
   354  	user = nca.rtDetails.User
   355  	password = nca.rtDetails.Password
   356  	// If access-token is defined, extract user from it.
   357  	rtDetails, err := nca.RtDetails()
   358  	if errorutils.CheckError(err) != nil {
   359  		return
   360  	}
   361  	if rtDetails.AccessToken != "" {
   362  		log.Debug("Using access-token details for nuget authentication.")
   363  		user, err = auth.ExtractUsernameFromAccessToken(rtDetails.AccessToken)
   364  		if err != nil {
   365  			return
   366  		}
   367  		password = rtDetails.AccessToken
   368  	}
   369  	return
   370  }
   371  
   372  func (nca *NugetCommandArgs) createNugetCmd() (*nuget.Cmd, error) {
   373  	c, err := nuget.NewNugetCmd()
   374  	if err != nil {
   375  		return nil, err
   376  	}
   377  	if nca.args != "" {
   378  		c.Command, err = shellwords.Parse(nca.args)
   379  		if err != nil {
   380  			return nil, errorutils.CheckError(err)
   381  		}
   382  	}
   383  
   384  	if nca.flags != "" {
   385  		c.CommandFlags, err = shellwords.Parse(nca.flags)
   386  	}
   387  
   388  	return c, errorutils.CheckError(err)
   389  }