github.com/jfrog/jfrog-cli-go@v1.22.1-0.20200318093948-4826ef344ffd/artifactory/utils/pip/setuppy.go (about)

     1  package pip
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	gofrogcmd "github.com/jfrog/gofrog/io"
     7  	"github.com/jfrog/jfrog-client-go/utils"
     8  	"github.com/jfrog/jfrog-client-go/utils/errorutils"
     9  	"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
    10  	"io/ioutil"
    11  	"os"
    12  )
    13  
    14  // Get the project-name by running 'egg_info' command on setup.py and extracting it from 'PKG-INFO' file.
    15  func ExtractPackageNameFromSetupPy(setuppyFilePath, pythonExecutablePath string) (string, error) {
    16  	// Execute egg_info command and return PKG-INFO content.
    17  	content, err := getEgginfoPkginfoContent(setuppyFilePath, pythonExecutablePath)
    18  	if err != nil {
    19  		return "", err
    20  	}
    21  
    22  	// Extract project name from file content.
    23  	return getProjectNameFromFileContent(content)
    24  }
    25  
    26  // Get package-name from PKG-INFO file content.
    27  // If pattern of package-name not found, return an error.
    28  func getProjectNameFromFileContent(content []byte) (string, error) {
    29  	// Create package-name regexp.
    30  	packageNameRegexp, err := utils.GetRegExp(`(?m)^Name\:\s(\w[\w-\.]+)`)
    31  	if err != nil {
    32  		return "", err
    33  	}
    34  
    35  	// Find first match of packageNameRegexp.
    36  	match := packageNameRegexp.FindStringSubmatch(string(content))
    37  	if len(match) < 2 {
    38  		return "", errorutils.CheckError(errors.New("Failed extracting package name from content."))
    39  	}
    40  
    41  	return match[1], nil
    42  }
    43  
    44  // Run egg-info command on setup.py, the command generates metadata files.
    45  // Return the content of the 'PKG-INFO' file.
    46  func getEgginfoPkginfoContent(setuppyFilePath, pythonExecutablePath string) ([]byte, error) {
    47  	tempDirPath, err := fileutils.CreateTempDir()
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	defer fileutils.RemoveTempDir(tempDirPath)
    52  
    53  	// Change work-dir to temp, preserve current work-dir when method ends.
    54  	wd, err := os.Getwd()
    55  	if errorutils.CheckError(err) != nil {
    56  		return nil, err
    57  	}
    58  	defer os.Chdir(wd)
    59  	err = os.Chdir(tempDirPath)
    60  	if errorutils.CheckError(err) != nil {
    61  		return nil, err
    62  	}
    63  
    64  	// Run python egg_info command.
    65  	egginfoOutput, err := executeEgginfoCommandWithOutput(pythonExecutablePath, setuppyFilePath)
    66  	if err != nil {
    67  		return nil, errorutils.CheckError(err)
    68  	}
    69  
    70  	// Parse egg_info execution output to find PKG-INFO path.
    71  	pkginfoPath, err := extractPkginfoPathFromCommandOutput(egginfoOutput)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  
    76  	// Read PKG-INFO file.
    77  	pkginfoFileExists, err := fileutils.IsFileExists(pkginfoPath, false)
    78  	if !pkginfoFileExists {
    79  		return nil, errorutils.CheckError(errors.New(fmt.Sprintf("File 'PKG-INFO' couldn't be found in its designated location: %s", pkginfoPath)))
    80  	}
    81  
    82  	return ioutil.ReadFile(pkginfoPath)
    83  }
    84  
    85  // Parse the output of 'python egg_info' command, in order to find the path of generated file 'PKG-INFO'.
    86  func extractPkginfoPathFromCommandOutput(egginfoOutput string) (string, error) {
    87  	// Regexp for extracting 'PKG-INFO' file-path from the 'egg_info' command output.
    88  	pkginfoRegexp, err := utils.GetRegExp(`(?m)writing\s(\w[\w-\.]+\.egg\-info[\\\/]PKG-INFO)`)
    89  	if err != nil {
    90  		return "", err
    91  	}
    92  
    93  	matchedOutputLines := pkginfoRegexp.FindAllString(egginfoOutput, -1)
    94  	if len(matchedOutputLines) != 1 {
    95  		return "", errorutils.CheckError(errors.New("Failed parsing egg_info command, couldn't find PKG-INFO location."))
    96  	}
    97  
    98  	// Extract path from matched line.
    99  	matchedResults := pkginfoRegexp.FindStringSubmatch(matchedOutputLines[0])
   100  	return matchedResults[1], nil
   101  }
   102  
   103  // Execute egg_info command for setup.py, return command's output.
   104  func executeEgginfoCommandWithOutput(pythonExecutablePath, setuppyFilePath string) (string, error) {
   105  	pythonEggInfoCmd := &PipCmd{
   106  		Executable:  pythonExecutablePath,
   107  		Command:     setuppyFilePath,
   108  		CommandArgs: []string{"egg_info"},
   109  	}
   110  	return gofrogcmd.RunCmdOutput(pythonEggInfoCmd)
   111  }