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