1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-09-16 09:26:22 +02:00

add support for toml file

This commit is contained in:
Christopher Fenner
2025-08-30 10:17:58 +02:00
parent 81966b0ec6
commit 0f95a45946

View File

@@ -3,17 +3,21 @@ package cmd
import ( import (
"fmt" "fmt"
"path/filepath" "path/filepath"
"strings"
"github.com/SAP/jenkins-library/pkg/buildsettings" "github.com/SAP/jenkins-library/pkg/buildsettings"
"github.com/SAP/jenkins-library/pkg/command" "github.com/SAP/jenkins-library/pkg/command"
"github.com/SAP/jenkins-library/pkg/feature"
"github.com/SAP/jenkins-library/pkg/log" "github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils" "github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/python"
"github.com/SAP/jenkins-library/pkg/telemetry" "github.com/SAP/jenkins-library/pkg/telemetry"
) )
const ( const (
PyBomFilename = "bom-pip.xml" PyBomFilename = "bom-pip.xml"
stepName = "pythonBuild" stepName = "pythonBuild"
cycloneDxVersion = "6.1.1"
cycloneDxPackageVersion = "cyclonedx-bom==6.1.1" cycloneDxPackageVersion = "cyclonedx-bom==6.1.1"
cycloneDxSchemaVersion = "1.4" cycloneDxSchemaVersion = "1.4"
) )
@@ -52,32 +56,49 @@ func pythonBuild(config pythonBuildOptions, telemetryData *telemetry.CustomData,
} }
func runPythonBuild(config *pythonBuildOptions, telemetryData *telemetry.CustomData, utils pythonBuildUtils, commonPipelineEnvironment *pythonBuildCommonPipelineEnvironment) error { func runPythonBuild(config *pythonBuildOptions, telemetryData *telemetry.CustomData, utils pythonBuildUtils, commonPipelineEnvironment *pythonBuildCommonPipelineEnvironment) error {
pipInstallFlags := []string{"install", "--upgrade"}
virutalEnvironmentPathMap := make(map[string]string) virutalEnvironmentPathMap := make(map[string]string)
err := createVirtualEnvironment(utils, config, virutalEnvironmentPathMap) // create virtualEnv
if err != nil { if err := createVirtualEnvironment(utils, config, virutalEnvironmentPathMap); err != nil {
return err return err
} }
//TODO: use a defer func to cleanup the virtual environment
err = buildExecute(config, utils, pipInstallFlags, virutalEnvironmentPathMap) // FEATURE FLAG (com_sap_piper_featureFlag_pythonToml) to switch to new implementation of python build step
if err != nil { if feature.IsFeatureEnabled("pythonToml") {
return fmt.Errorf("Python build failed with error: %w", err) // check project descriptor
buildDescriptorFilePath, err := searchDescriptor([]string{"pyproject.toml", "setup.py"}, utils.FileExists)
if err != nil {
return err
}
// build package
if strings.HasSuffix(buildDescriptorFilePath, "pyproject.toml") {
if err := python.InstallProjectDependencies(utils.RunExecutable, python.Binary); err != nil {
return fmt.Errorf("Failed to install project dependencies: %w", err)
}
if err := python.Build(utils.RunExecutable, python.Binary, config.BuildFlags, config.SetupFlags); err != nil {
return fmt.Errorf("Failed to build python project: %w", err)
}
}
} else {
if err := buildExecute(config, utils, virutalEnvironmentPathMap); err != nil {
return fmt.Errorf("Python build failed with error: %w", err)
}
} }
// generate BOM
if config.CreateBOM { if config.CreateBOM {
if err := runBOMCreationForPy(utils, pipInstallFlags, virutalEnvironmentPathMap, config); err != nil { if err := runBOMCreationForPy(utils, virutalEnvironmentPathMap, config); err != nil {
return fmt.Errorf("BOM creation failed: %w", err) return fmt.Errorf("BOM creation failed: %w", err)
} }
} }
// generate build settings information
log.Entry().Debugf("creating build settings information...") log.Entry().Debugf("creating build settings information...")
dockerImage, err := GetDockerImageValue(stepName) dockerImage, err := GetDockerImageValue(stepName)
if err != nil { if err != nil {
return err return err
} }
pythonConfig := buildsettings.BuildOptions{ pythonConfig := buildsettings.BuildOptions{
CreateBOM: config.CreateBOM, CreateBOM: config.CreateBOM,
Publish: config.Publish, Publish: config.Publish,
@@ -90,21 +111,18 @@ func runPythonBuild(config *pythonBuildOptions, telemetryData *telemetry.CustomD
} }
commonPipelineEnvironment.custom.buildSettingsInfo = buildSettingsInfo commonPipelineEnvironment.custom.buildSettingsInfo = buildSettingsInfo
// publish package
if config.Publish { if config.Publish {
if err := publishWithTwine(config, utils, pipInstallFlags, virutalEnvironmentPathMap); err != nil { if err := publishWithTwine(config, utils, virutalEnvironmentPathMap); err != nil {
return fmt.Errorf("failed to publish: %w", err) return fmt.Errorf("failed to publish: %w", err)
} }
} }
err = removeVirtualEnvironment(utils, config) // remove virtualEnv
if err != nil { return removeVirtualEnvironment(utils, config)
return err
}
return nil
} }
func buildExecute(config *pythonBuildOptions, utils pythonBuildUtils, pipInstallFlags []string, virutalEnvironmentPathMap map[string]string) error { func buildExecute(config *pythonBuildOptions, utils pythonBuildUtils, virutalEnvironmentPathMap map[string]string) error {
var flags []string var flags []string
flags = append(flags, config.BuildFlags...) flags = append(flags, config.BuildFlags...)
flags = append(flags, "setup.py") flags = append(flags, "setup.py")
@@ -138,18 +156,17 @@ func createVirtualEnvironment(utils pythonBuildUtils, config *pythonBuildOptions
} }
func removeVirtualEnvironment(utils pythonBuildUtils, config *pythonBuildOptions) error { func removeVirtualEnvironment(utils pythonBuildUtils, config *pythonBuildOptions) error {
err := utils.RemoveAll(config.VirutalEnvironmentName) if err := utils.RemoveAll(config.VirutalEnvironmentName); err != nil {
if err != nil { return fmt.Errorf("failed to remove virtual environment: %w", err)
return err
} }
return nil return nil
} }
func runBOMCreationForPy(utils pythonBuildUtils, pipInstallFlags []string, virutalEnvironmentPathMap map[string]string, config *pythonBuildOptions) error { func runBOMCreationForPy(utils pythonBuildUtils, virutalEnvironmentPathMap map[string]string, config *pythonBuildOptions) error {
pipInstallOriginalFlags := pipInstallFlags // install dependencies from requirements.txt
exists, _ := utils.FileExists(config.RequirementsFilePath) exists, _ := utils.FileExists(config.RequirementsFilePath)
if exists { if exists {
pipInstallRequirementsFlags := append(pipInstallOriginalFlags, "--requirement", config.RequirementsFilePath) pipInstallRequirementsFlags := append(python.PipInstallFlags, "--requirement", config.RequirementsFilePath)
if err := utils.RunExecutable(virutalEnvironmentPathMap["pip"], pipInstallRequirementsFlags...); err != nil { if err := utils.RunExecutable(virutalEnvironmentPathMap["pip"], pipInstallRequirementsFlags...); err != nil {
return err return err
} }
@@ -157,29 +174,58 @@ func runBOMCreationForPy(utils pythonBuildUtils, pipInstallFlags []string, virut
log.Entry().Warnf("unable to find requirements.txt file at %s , continuing SBOM generation without requirements.txt", config.RequirementsFilePath) log.Entry().Warnf("unable to find requirements.txt file at %s , continuing SBOM generation without requirements.txt", config.RequirementsFilePath)
} }
pipInstallCycloneDxFlags := append(pipInstallOriginalFlags, cycloneDxPackageVersion) // install cyclonedx
pipInstallCycloneDxFlags := append(python.PipInstallFlags, cycloneDxPackageVersion)
if err := utils.RunExecutable(virutalEnvironmentPathMap["pip"], pipInstallCycloneDxFlags...); err != nil { if err := utils.RunExecutable(virutalEnvironmentPathMap["pip"], pipInstallCycloneDxFlags...); err != nil {
return err return err
} }
virutalEnvironmentPathMap["cyclonedx"] = filepath.Join(config.VirutalEnvironmentName, "bin", "cyclonedx-py") virutalEnvironmentPathMap["cyclonedx"] = filepath.Join(config.VirutalEnvironmentName, "bin", "cyclonedx-py")
if err := utils.RunExecutable(virutalEnvironmentPathMap["cyclonedx"], "env", "--output-file", PyBomFilename, "--output-format", "XML", "--spec-version", cycloneDxSchemaVersion); err != nil { // run cyclonedx
// TODO: use modules, python -m cyclonedx_py ... to avoid virutalEnvironmentPathMap
if err := utils.RunExecutable(
virutalEnvironmentPathMap["cyclonedx"],
"env",
"--output-file", PyBomFilename,
"--output-format", "XML",
"--spec-version", cycloneDxSchemaVersion,
); err != nil {
return err return err
} }
return nil return nil
} }
func publishWithTwine(config *pythonBuildOptions, utils pythonBuildUtils, pipInstallFlags []string, virutalEnvironmentPathMap map[string]string) error { func publishWithTwine(config *pythonBuildOptions, utils pythonBuildUtils, virutalEnvironmentPathMap map[string]string) error {
pipInstallFlags = append(pipInstallFlags, "twine") if err := python.Install(utils.RunExecutable, "twine", "", config.VirutalEnvironmentName, virutalEnvironmentPathMap); err != nil {
if err := utils.RunExecutable(virutalEnvironmentPathMap["pip"], pipInstallFlags...); err != nil {
return err return err
} }
virutalEnvironmentPathMap["twine"] = filepath.Join(config.VirutalEnvironmentName, "bin", "twine")
if err := utils.RunExecutable(virutalEnvironmentPathMap["twine"], "upload", "--username", config.TargetRepositoryUser, // TODO: use modules, python -m twine ... to avoid virutalEnvironmentPathMap
"--password", config.TargetRepositoryPassword, "--repository-url", config.TargetRepositoryURL, "--disable-progress-bar", if err := utils.RunExecutable(
"dist/*"); err != nil { virutalEnvironmentPathMap["twine"],
"upload",
"--username", config.TargetRepositoryUser,
"--password", config.TargetRepositoryPassword,
"--repository-url", config.TargetRepositoryURL,
"--disable-progress-bar",
"dist/*",
); err != nil {
return err return err
} }
return nil return nil
} }
func searchDescriptor(supported []string, existsFunc func(string) (bool, error)) (string, error) {
var descriptor string
for _, f := range supported {
exists, _ := existsFunc(f)
if exists {
descriptor = f
break
}
}
if len(descriptor) == 0 {
return "", fmt.Errorf("no build descriptor available, supported: %v", supported)
}
return descriptor, nil
}