github.com/jaylevin/jenkins-library@v1.230.4/cmd/pythonBuild.go (about)

     1  package cmd
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  
     7  	"github.com/SAP/jenkins-library/pkg/buildsettings"
     8  	"github.com/SAP/jenkins-library/pkg/command"
     9  	"github.com/SAP/jenkins-library/pkg/log"
    10  	"github.com/SAP/jenkins-library/pkg/piperutils"
    11  	"github.com/SAP/jenkins-library/pkg/telemetry"
    12  )
    13  
    14  const (
    15  	PyBomFilename = "bom.xml"
    16  	stepName      = "pythonBuild"
    17  )
    18  
    19  type pythonBuildUtils interface {
    20  	command.ExecRunner
    21  	FileExists(filename string) (bool, error)
    22  	piperutils.FileUtils
    23  }
    24  
    25  type pythonBuildUtilsBundle struct {
    26  	*command.Command
    27  	*piperutils.Files
    28  }
    29  
    30  func newPythonBuildUtils() pythonBuildUtils {
    31  	utils := pythonBuildUtilsBundle{
    32  		Command: &command.Command{},
    33  		Files:   &piperutils.Files{},
    34  	}
    35  	// Reroute command output to logging framework
    36  	utils.Stdout(log.Writer())
    37  	utils.Stderr(log.Writer())
    38  	return &utils
    39  }
    40  
    41  func pythonBuild(config pythonBuildOptions, telemetryData *telemetry.CustomData, commonPipelineEnvironment *pythonBuildCommonPipelineEnvironment) {
    42  	utils := newPythonBuildUtils()
    43  
    44  	err := runPythonBuild(&config, telemetryData, utils, commonPipelineEnvironment)
    45  	if err != nil {
    46  		log.Entry().WithError(err).Fatal("step execution failed")
    47  	}
    48  }
    49  
    50  func runPythonBuild(config *pythonBuildOptions, telemetryData *telemetry.CustomData, utils pythonBuildUtils, commonPipelineEnvironment *pythonBuildCommonPipelineEnvironment) error {
    51  
    52  	pipInstallFlags := []string{"install", "--upgrade"}
    53  	virutalEnvironmentPathMap := make(map[string]string)
    54  
    55  	err := createVirtualEnvironment(utils, config, virutalEnvironmentPathMap)
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	err = buildExecute(config, utils, pipInstallFlags, virutalEnvironmentPathMap)
    61  	if err != nil {
    62  		return fmt.Errorf("Python build failed with error: %w", err)
    63  	}
    64  
    65  	if config.CreateBOM {
    66  		if err := runBOMCreationForPy(utils, pipInstallFlags, virutalEnvironmentPathMap, config); err != nil {
    67  			return fmt.Errorf("BOM creation failed: %w", err)
    68  		}
    69  	}
    70  
    71  	log.Entry().Debugf("creating build settings information...")
    72  
    73  	dockerImage, err := GetDockerImageValue(stepName)
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	pythonConfig := buildsettings.BuildOptions{
    79  		CreateBOM:         config.CreateBOM,
    80  		Publish:           config.Publish,
    81  		BuildSettingsInfo: config.BuildSettingsInfo,
    82  		DockerImage:       dockerImage,
    83  	}
    84  	buildSettingsInfo, err := buildsettings.CreateBuildSettingsInfo(&pythonConfig, stepName)
    85  	if err != nil {
    86  		log.Entry().Warnf("failed to create build settings info: %v", err)
    87  	}
    88  	commonPipelineEnvironment.custom.buildSettingsInfo = buildSettingsInfo
    89  
    90  	if config.Publish {
    91  		if err := publishWithTwine(config, utils, pipInstallFlags, virutalEnvironmentPathMap); err != nil {
    92  			return fmt.Errorf("failed to publish: %w", err)
    93  		}
    94  	}
    95  
    96  	err = removeVirtualEnvironment(utils, config)
    97  	if err != nil {
    98  		return err
    99  	}
   100  
   101  	return nil
   102  }
   103  
   104  func buildExecute(config *pythonBuildOptions, utils pythonBuildUtils, pipInstallFlags []string, virutalEnvironmentPathMap map[string]string) error {
   105  
   106  	var flags []string
   107  	flags = append(flags, config.BuildFlags...)
   108  	flags = append(flags, "setup.py", "sdist", "bdist_wheel")
   109  
   110  	log.Entry().Info("starting building python project:")
   111  	err := utils.RunExecutable(virutalEnvironmentPathMap["python"], flags...)
   112  	if err != nil {
   113  		return err
   114  	}
   115  	return nil
   116  }
   117  
   118  func createVirtualEnvironment(utils pythonBuildUtils, config *pythonBuildOptions, virutalEnvironmentPathMap map[string]string) error {
   119  	virtualEnvironmentFlags := []string{"-m", "venv", config.VirutalEnvironmentName}
   120  	err := utils.RunExecutable("python3", virtualEnvironmentFlags...)
   121  	if err != nil {
   122  		return err
   123  	}
   124  	err = utils.RunExecutable("bash", "-c", "source "+filepath.Join(config.VirutalEnvironmentName, "bin", "activate"))
   125  	if err != nil {
   126  		return err
   127  	}
   128  	virutalEnvironmentPathMap["pip"] = filepath.Join(config.VirutalEnvironmentName, "bin", "pip")
   129  	// venv will create symlinks to python3 inside the container
   130  	virutalEnvironmentPathMap["python"] = "python"
   131  	virutalEnvironmentPathMap["deactivate"] = filepath.Join(config.VirutalEnvironmentName, "bin", "deactivate")
   132  
   133  	return nil
   134  }
   135  
   136  func removeVirtualEnvironment(utils pythonBuildUtils, config *pythonBuildOptions) error {
   137  	err := utils.RemoveAll(config.VirutalEnvironmentName)
   138  	if err != nil {
   139  		return err
   140  	}
   141  	return nil
   142  }
   143  
   144  func runBOMCreationForPy(utils pythonBuildUtils, pipInstallFlags []string, virutalEnvironmentPathMap map[string]string, config *pythonBuildOptions) error {
   145  	pipInstallFlags = append(pipInstallFlags, "cyclonedx-bom")
   146  	if err := utils.RunExecutable(virutalEnvironmentPathMap["pip"], pipInstallFlags...); err != nil {
   147  		return err
   148  	}
   149  	virutalEnvironmentPathMap["cyclonedx"] = filepath.Join(config.VirutalEnvironmentName, "bin", "cyclonedx-bom")
   150  
   151  	if err := utils.RunExecutable(virutalEnvironmentPathMap["cyclonedx"], "--e", "--output", PyBomFilename); err != nil {
   152  		return err
   153  	}
   154  	return nil
   155  }
   156  
   157  func publishWithTwine(config *pythonBuildOptions, utils pythonBuildUtils, pipInstallFlags []string, virutalEnvironmentPathMap map[string]string) error {
   158  	pipInstallFlags = append(pipInstallFlags, "twine")
   159  	if err := utils.RunExecutable(virutalEnvironmentPathMap["pip"], pipInstallFlags...); err != nil {
   160  		return err
   161  	}
   162  	virutalEnvironmentPathMap["twine"] = filepath.Join(config.VirutalEnvironmentName, "bin", "twine")
   163  	if err := utils.RunExecutable(virutalEnvironmentPathMap["twine"], "upload", "--username", config.TargetRepositoryUser,
   164  		"--password", config.TargetRepositoryPassword, "--repository-url", config.TargetRepositoryURL,
   165  		"dist/*"); err != nil {
   166  		return err
   167  	}
   168  	return nil
   169  }