1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-10-08 23:02:10 +02:00

refactor(python): create python package (#5482)

This commit is contained in:
Christopher Fenner
2025-09-23 09:59:21 +02:00
committed by GitHub
parent de63042799
commit 6740963b68
13 changed files with 503 additions and 158 deletions

View File

@@ -2,20 +2,19 @@ package cmd
import (
"fmt"
"path/filepath"
"github.com/SAP/jenkins-library/pkg/buildsettings"
"github.com/SAP/jenkins-library/pkg/command"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/python"
"github.com/SAP/jenkins-library/pkg/telemetry"
)
const (
PyBomFilename = "bom-pip.xml"
stepName = "pythonBuild"
cycloneDxPackageVersion = "cyclonedx-bom==6.1.1"
cycloneDxSchemaVersion = "1.4"
cycloneDxVersion = "6.1.1"
cycloneDxSchemaVersion = "1.4"
stepName = "pythonBuild"
)
type pythonBuildUtils interface {
@@ -32,7 +31,7 @@ type pythonBuildUtilsBundle struct {
func newPythonBuildUtils() pythonBuildUtils {
utils := pythonBuildUtilsBundle{
Command: &command.Command{
StepName: "pythonBuild",
StepName: stepName,
},
Files: &piperutils.Files{},
}
@@ -52,30 +51,49 @@ func pythonBuild(config pythonBuildOptions, telemetryData *telemetry.CustomData,
}
func runPythonBuild(config *pythonBuildOptions, telemetryData *telemetry.CustomData, utils pythonBuildUtils, commonPipelineEnvironment *pythonBuildCommonPipelineEnvironment) error {
pipInstallFlags := []string{"install", "--upgrade"}
virutalEnvironmentPathMap := make(map[string]string)
if exitHandler, err := python.CreateVirtualEnvironment(utils.RunExecutable, utils.RemoveAll, config.VirtualEnvironmentName); err != nil {
return err
} else {
log.DeferExitHandler(exitHandler)
defer exitHandler()
}
err := createVirtualEnvironment(utils, config, virutalEnvironmentPathMap)
if err != nil {
if err := python.BuildWithSetupPy(utils.RunExecutable, config.VirtualEnvironmentName, config.BuildFlags, config.SetupFlags); err != nil {
return err
}
err = buildExecute(config, utils, pipInstallFlags, virutalEnvironmentPathMap)
if err != nil {
return fmt.Errorf("Python build failed with error: %w", err)
}
if config.CreateBOM {
if err := runBOMCreationForPy(utils, pipInstallFlags, virutalEnvironmentPathMap, config); err != nil {
if err := python.CreateBOM(utils.RunExecutable, utils.FileExists, config.VirtualEnvironmentName, config.RequirementsFilePath, cycloneDxVersion, cycloneDxSchemaVersion); err != nil {
return fmt.Errorf("BOM creation failed: %w", err)
}
}
log.Entry().Debugf("creating build settings information...")
if info, err := createBuildSettingsInfo(config); err != nil {
return err
} else {
commonPipelineEnvironment.custom.buildSettingsInfo = info
}
if config.Publish {
if err := python.PublishPackage(
utils.RunExecutable,
config.VirtualEnvironmentName,
config.TargetRepositoryURL,
config.TargetRepositoryUser,
config.TargetRepositoryPassword,
); err != nil {
return fmt.Errorf("failed to publish: %w", err)
}
}
return nil
}
// TODO: extract to common place
func createBuildSettingsInfo(config *pythonBuildOptions) (string, error) {
log.Entry().Debugf("creating build settings information...")
dockerImage, err := GetDockerImageValue(stepName)
if err != nil {
return err
return "", err
}
pythonConfig := buildsettings.BuildOptions{
@@ -88,98 +106,5 @@ func runPythonBuild(config *pythonBuildOptions, telemetryData *telemetry.CustomD
if err != nil {
log.Entry().Warnf("failed to create build settings info: %v", err)
}
commonPipelineEnvironment.custom.buildSettingsInfo = buildSettingsInfo
if config.Publish {
if err := publishWithTwine(config, utils, pipInstallFlags, virutalEnvironmentPathMap); err != nil {
return fmt.Errorf("failed to publish: %w", err)
}
}
err = removeVirtualEnvironment(utils, config)
if err != nil {
return err
}
return nil
}
func buildExecute(config *pythonBuildOptions, utils pythonBuildUtils, pipInstallFlags []string, virutalEnvironmentPathMap map[string]string) error {
var flags []string
flags = append(flags, config.BuildFlags...)
flags = append(flags, "setup.py")
flags = append(flags, config.SetupFlags...)
flags = append(flags, "sdist", "bdist_wheel")
log.Entry().Info("starting building python project:")
err := utils.RunExecutable(virutalEnvironmentPathMap["python"], flags...)
if err != nil {
return err
}
return nil
}
func createVirtualEnvironment(utils pythonBuildUtils, config *pythonBuildOptions, virutalEnvironmentPathMap map[string]string) error {
virtualEnvironmentFlags := []string{"-m", "venv", config.VirtualEnvironmentName}
err := utils.RunExecutable("python3", virtualEnvironmentFlags...)
if err != nil {
return err
}
err = utils.RunExecutable("bash", "-c", "source "+filepath.Join(config.VirtualEnvironmentName, "bin", "activate"))
if err != nil {
return err
}
virutalEnvironmentPathMap["pip"] = filepath.Join(config.VirtualEnvironmentName, "bin", "pip")
// venv will create symlinks to python3 inside the container
virutalEnvironmentPathMap["python"] = "python"
virutalEnvironmentPathMap["deactivate"] = filepath.Join(config.VirtualEnvironmentName, "bin", "deactivate")
return nil
}
func removeVirtualEnvironment(utils pythonBuildUtils, config *pythonBuildOptions) error {
err := utils.RemoveAll(config.VirtualEnvironmentName)
if err != nil {
return err
}
return nil
}
func runBOMCreationForPy(utils pythonBuildUtils, pipInstallFlags []string, virutalEnvironmentPathMap map[string]string, config *pythonBuildOptions) error {
pipInstallOriginalFlags := pipInstallFlags
exists, _ := utils.FileExists(config.RequirementsFilePath)
if exists {
pipInstallRequirementsFlags := append(pipInstallOriginalFlags, "--requirement", config.RequirementsFilePath)
if err := utils.RunExecutable(virutalEnvironmentPathMap["pip"], pipInstallRequirementsFlags...); err != nil {
return err
}
} else {
log.Entry().Warnf("unable to find requirements.txt file at %s , continuing SBOM generation without requirements.txt", config.RequirementsFilePath)
}
pipInstallCycloneDxFlags := append(pipInstallOriginalFlags, cycloneDxPackageVersion)
if err := utils.RunExecutable(virutalEnvironmentPathMap["pip"], pipInstallCycloneDxFlags...); err != nil {
return err
}
virutalEnvironmentPathMap["cyclonedx"] = filepath.Join(config.VirtualEnvironmentName, "bin", "cyclonedx-py")
if err := utils.RunExecutable(virutalEnvironmentPathMap["cyclonedx"], "env", "--output-file", PyBomFilename, "--output-format", "XML", "--spec-version", cycloneDxSchemaVersion); err != nil {
return err
}
return nil
}
func publishWithTwine(config *pythonBuildOptions, utils pythonBuildUtils, pipInstallFlags []string, virutalEnvironmentPathMap map[string]string) error {
pipInstallFlags = append(pipInstallFlags, "twine")
if err := utils.RunExecutable(virutalEnvironmentPathMap["pip"], pipInstallFlags...); err != nil {
return err
}
virutalEnvironmentPathMap["twine"] = filepath.Join(config.VirtualEnvironmentName, "bin", "twine")
if err := utils.RunExecutable(virutalEnvironmentPathMap["twine"], "upload", "--username", config.TargetRepositoryUser,
"--password", config.TargetRepositoryPassword, "--repository-url", config.TargetRepositoryURL, "--disable-progress-bar",
"dist/*"); err != nil {
return err
}
return nil
return buildSettingsInfo, nil
}

View File

@@ -53,7 +53,7 @@ func TestRunPythonBuild(t *testing.T) {
telemetryData := telemetry.CustomData{}
err := runPythonBuild(&config, &telemetryData, utils, &cpe)
assert.EqualError(t, err, "Python build failed with error: build failure")
assert.EqualError(t, err, "failed to build package: build failure")
})
t.Run("success - publishes binaries", func(t *testing.T) {
@@ -72,14 +72,16 @@ func TestRunPythonBuild(t *testing.T) {
assert.Equal(t, []string{"-m", "venv", config.VirtualEnvironmentName}, utils.ExecMockRunner.Calls[0].Params)
assert.Equal(t, "bash", utils.ExecMockRunner.Calls[1].Exec)
assert.Equal(t, []string{"-c", "source " + filepath.Join("dummy", "bin", "activate")}, utils.ExecMockRunner.Calls[1].Params)
assert.Equal(t, "python", utils.ExecMockRunner.Calls[2].Exec)
assert.Equal(t, []string{"setup.py", "sdist", "bdist_wheel"}, utils.ExecMockRunner.Calls[2].Params)
assert.Equal(t, filepath.Join("dummy", "bin", "pip"), utils.ExecMockRunner.Calls[3].Exec)
assert.Equal(t, []string{"install", "--upgrade", "twine"}, utils.ExecMockRunner.Calls[3].Params)
assert.Equal(t, filepath.Join("dummy", "bin", "twine"), utils.ExecMockRunner.Calls[4].Exec)
assert.Equal(t, "dummy/bin/pip", utils.ExecMockRunner.Calls[2].Exec)
assert.Equal(t, []string{"install", "--upgrade", "--root-user-action=ignore", "wheel"}, utils.ExecMockRunner.Calls[2].Params)
assert.Equal(t, "dummy/bin/python", utils.ExecMockRunner.Calls[3].Exec)
assert.Equal(t, []string{"setup.py", "sdist", "bdist_wheel"}, utils.ExecMockRunner.Calls[3].Params)
assert.Equal(t, filepath.Join("dummy", "bin", "pip"), utils.ExecMockRunner.Calls[4].Exec)
assert.Equal(t, []string{"install", "--upgrade", "--root-user-action=ignore", "twine"}, utils.ExecMockRunner.Calls[4].Params)
assert.Equal(t, filepath.Join("dummy", "bin", "twine"), utils.ExecMockRunner.Calls[5].Exec)
assert.Equal(t, []string{"upload", "--username", config.TargetRepositoryUser,
"--password", config.TargetRepositoryPassword, "--repository-url", config.TargetRepositoryURL,
"--disable-progress-bar", "dist/*"}, utils.ExecMockRunner.Calls[4].Params)
"--disable-progress-bar", "dist/*"}, utils.ExecMockRunner.Calls[5].Params)
})
t.Run("success - create BOM", func(t *testing.T) {
@@ -97,42 +99,13 @@ func TestRunPythonBuild(t *testing.T) {
assert.Equal(t, []string{"-m", "venv", config.VirtualEnvironmentName}, utils.ExecMockRunner.Calls[0].Params)
assert.Equal(t, "bash", utils.ExecMockRunner.Calls[1].Exec)
assert.Equal(t, []string{"-c", "source " + filepath.Join("dummy", "bin", "activate")}, utils.ExecMockRunner.Calls[1].Params)
assert.Equal(t, "python", utils.ExecMockRunner.Calls[2].Exec)
assert.Equal(t, []string{"setup.py", "sdist", "bdist_wheel"}, utils.ExecMockRunner.Calls[2].Params)
assert.Equal(t, filepath.Join("dummy", "bin", "pip"), utils.ExecMockRunner.Calls[3].Exec)
assert.Equal(t, []string{"install", "--upgrade", "cyclonedx-bom==6.1.1"}, utils.ExecMockRunner.Calls[3].Params)
assert.Equal(t, filepath.Join("dummy", "bin", "cyclonedx-py"), utils.ExecMockRunner.Calls[4].Exec)
assert.Equal(t, []string{"env", "--output-file", "bom-pip.xml", "--output-format", "XML", "--spec-version", "1.4"}, utils.ExecMockRunner.Calls[4].Params)
})
}
func TestPythonBuildExecute(t *testing.T) {
t.Run("Test build with flags", func(t *testing.T) {
config := pythonBuildOptions{
BuildFlags: []string{"--verbose"},
SetupFlags: []string{"egg_info", "--tag-build=pr13"},
VirtualEnvironmentName: "venv",
}
utils := pythonBuildMockUtils{
ExecMockRunner: &mock.ExecMockRunner{},
}
virutalEnvironmentPathMap := map[string]string{
"python": "python",
}
err := buildExecute(&config, &utils, []string{}, virutalEnvironmentPathMap)
assert.NoError(t, err)
assert.Equal(t, "python", utils.ExecMockRunner.Calls[0].Exec)
assert.Equal(t, []string{
"--verbose",
"setup.py",
"egg_info",
"--tag-build=pr13",
"sdist",
"bdist_wheel",
}, utils.ExecMockRunner.Calls[0].Params)
assert.Equal(t, "dummy/bin/pip", utils.ExecMockRunner.Calls[2].Exec)
assert.Equal(t, []string{"install", "--upgrade", "--root-user-action=ignore", "wheel"}, utils.ExecMockRunner.Calls[2].Params)
assert.Equal(t, "dummy/bin/python", utils.ExecMockRunner.Calls[3].Exec)
assert.Equal(t, []string{"setup.py", "sdist", "bdist_wheel"}, utils.ExecMockRunner.Calls[3].Params)
assert.Equal(t, filepath.Join("dummy", "bin", "pip"), utils.ExecMockRunner.Calls[4].Exec)
assert.Equal(t, []string{"install", "--upgrade", "--root-user-action=ignore", "cyclonedx-bom==6.1.1"}, utils.ExecMockRunner.Calls[4].Params)
assert.Equal(t, filepath.Join("dummy", "bin", "cyclonedx-py"), utils.ExecMockRunner.Calls[5].Exec)
assert.Equal(t, []string{"env", "--output-file", "bom-pip.xml", "--output-format", "XML", "--spec-version", "1.4"}, utils.ExecMockRunner.Calls[5].Params)
})
}

View File

@@ -64,8 +64,8 @@ func TestPythonIntegrationBuildProject(t *testing.T) {
}
output := string(content)
assert.Contains(t, output, "info pythonBuild - running command: python setup.py sdist bdist_wheel")
assert.Contains(t, output, "info pythonBuild - running command: piperBuild-env/bin/pip install --upgrade cyclonedx-bom")
assert.Contains(t, output, "info pythonBuild - running command: piperBuild-env/bin/python setup.py sdist bdist_wheel")
assert.Contains(t, output, "info pythonBuild - running command: piperBuild-env/bin/pip install --upgrade --root-user-action=ignore cyclonedx-bom==")
assert.Contains(t, output, "info pythonBuild - running command: piperBuild-env/bin/cyclonedx-py env --output-file bom-pip.xml")
assert.Contains(t, output, "info pythonBuild - SUCCESS")

49
pkg/python/bom.go Normal file
View File

@@ -0,0 +1,49 @@
package python
import (
"fmt"
"path/filepath"
"github.com/SAP/jenkins-library/pkg/log"
)
const (
BOMFilename = "bom-pip.xml"
)
func CreateBOM(
executeFn func(executable string, params ...string) error,
existsFn func(path string) (bool, error),
virtualEnv string,
requirementsFile string,
cycloneDxVersion string,
cycloneDxSchemaVersion string,
) error {
if exists, _ := existsFn(requirementsFile); exists {
if err := InstallRequirements(executeFn, virtualEnv, requirementsFile); err != nil {
return err
}
} else {
log.Entry().Warnf("unable to find requirements.txt file at %s , continuing SBOM generation without requirements.txt", requirementsFile)
}
if err := InstallCycloneDX(executeFn, virtualEnv, cycloneDxVersion); err != nil {
return err
}
cycloneDxBinary := "cyclonedx-py"
if len(virtualEnv) > 0 {
cycloneDxBinary = filepath.Join(virtualEnv, "bin", cycloneDxBinary)
}
log.Entry().Debug("creating BOM")
if err := executeFn(cycloneDxBinary,
"env",
"--output-file", BOMFilename,
"--output-format", "XML",
"--spec-version", cycloneDxSchemaVersion,
); err != nil {
return fmt.Errorf("failed to create BOM: %w", err)
}
return nil
}

36
pkg/python/bom_test.go Normal file
View File

@@ -0,0 +1,36 @@
//go:build unit
// +build unit
package python
import (
"testing"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/stretchr/testify/assert"
)
func TestCreateBOM(t *testing.T) {
// init
mockRunner := mock.ExecMockRunner{}
mockFiles := mock.FilesMock{}
// test
err := CreateBOM(mockRunner.RunExecutable, mockFiles.FileExists, ".venv", "requirements.txt", "1.2.3", "16")
// assert
assert.NoError(t, err)
assert.Len(t, mockRunner.Calls, 2)
assert.Equal(t, ".venv/bin/pip", mockRunner.Calls[0].Exec)
assert.Equal(t, []string{
"install",
"--upgrade",
"--root-user-action=ignore",
"cyclonedx-bom==1.2.3"}, mockRunner.Calls[0].Params)
assert.Equal(t, ".venv/bin/cyclonedx-py", mockRunner.Calls[1].Exec)
assert.Equal(t, []string{
"env",
"--output-file", "bom-pip.xml",
"--output-format", "XML",
"--spec-version", "16"}, mockRunner.Calls[1].Params)
}

37
pkg/python/build.go Normal file
View File

@@ -0,0 +1,37 @@
package python
import (
"fmt"
"path/filepath"
"github.com/SAP/jenkins-library/pkg/log"
)
func BuildWithSetupPy(
executeFn func(executable string, params ...string) error,
virtualEnv string,
pythonArgs []string,
setupArgs []string,
) error {
// install dependency
if err := InstallWheel(executeFn, virtualEnv); err != nil {
return err
}
pythonBinary := "python"
if len(virtualEnv) > 0 {
pythonBinary = filepath.Join(virtualEnv, "bin", pythonBinary)
}
var flags []string
flags = append(flags, pythonArgs...)
flags = append(flags, "setup.py")
flags = append(flags, setupArgs...)
flags = append(flags, "sdist", "bdist_wheel")
log.Entry().Debug("building project")
if err := executeFn(pythonBinary, flags...); err != nil {
return fmt.Errorf("failed to build package: %w", err)
}
return nil
}

40
pkg/python/build_test.go Normal file
View File

@@ -0,0 +1,40 @@
//go:build unit
// +build unit
package python
import (
"testing"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/stretchr/testify/assert"
)
func TestBuildWithSetupPy(t *testing.T) {
// init
mockRunner := mock.ExecMockRunner{}
buildFlags := []string{"--verbose"}
setupFlags := []string{"egg_info", "--tag-build=pr13"}
// test
err := BuildWithSetupPy(mockRunner.RunExecutable, ".venv", buildFlags, setupFlags)
// assert
assert.NoError(t, err)
assert.Len(t, mockRunner.Calls, 2)
assert.Equal(t, ".venv/bin/pip", mockRunner.Calls[0].Exec)
assert.Equal(t, []string{
"install",
"--upgrade",
"--root-user-action=ignore",
"wheel"}, mockRunner.Calls[0].Params)
assert.Equal(t, ".venv/bin/python", mockRunner.Calls[1].Exec)
assert.Equal(t, []string{
"--verbose",
"setup.py",
"egg_info",
"--tag-build=pr13",
"sdist",
"bdist_wheel",
}, mockRunner.Calls[1].Params)
}

29
pkg/python/env.go Normal file
View File

@@ -0,0 +1,29 @@
package python
import (
"fmt"
"path/filepath"
"github.com/SAP/jenkins-library/pkg/log"
)
func CreateVirtualEnvironment(
executeFn func(executable string, params ...string) error,
removeFn func(executable string) error,
virtualEnv string,
) (func(), error) {
exitHandler := func() {
if err := removeFn(virtualEnv); err != nil {
log.Entry().Debugf("failed to remove virtual environment %s: %v", virtualEnv, err)
}
}
// Implementation for creating a virtual environment
if err := executeFn("python3", "-m", "venv", virtualEnv); err != nil {
return exitHandler, fmt.Errorf("failed to create virtual environment %s: %w", virtualEnv, err)
}
if err := executeFn("bash", "-c", fmt.Sprintf("source %s", filepath.Join(virtualEnv, "bin", "activate"))); err != nil {
return exitHandler, fmt.Errorf("failed to activate virtual environment %s: %w", virtualEnv, err)
}
return exitHandler, nil
}

28
pkg/python/env_test.go Normal file
View File

@@ -0,0 +1,28 @@
//go:build unit
// +build unit
package python
import (
"testing"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/stretchr/testify/assert"
)
func TestCreateVirtualEnvironment(t *testing.T) {
// init
mockRunner := mock.ExecMockRunner{}
mockFiles := mock.FilesMock{}
// test
_, err := CreateVirtualEnvironment(mockRunner.RunExecutable, mockFiles.RemoveAll, ".venv")
// assert
assert.NoError(t, err)
assert.Len(t, mockRunner.Calls, 2)
assert.Equal(t, "python3", mockRunner.Calls[0].Exec)
assert.Equal(t, []string{"-m", "venv", ".venv"}, mockRunner.Calls[0].Params)
assert.Equal(t, "bash", mockRunner.Calls[1].Exec)
assert.Equal(t, []string{"-c", "source .venv/bin/activate"}, mockRunner.Calls[1].Params)
}

77
pkg/python/pip.go Normal file
View File

@@ -0,0 +1,77 @@
package python
import (
"fmt"
"path/filepath"
"github.com/SAP/jenkins-library/pkg/log"
)
var (
PipInstallFlags = []string{"install", "--upgrade", "--root-user-action=ignore"}
)
func Install(
executeFn func(executable string, params ...string) error,
virtualEnv string,
module string,
version string,
extraArgs []string,
) error {
pipBinary := "pip"
if len(virtualEnv) > 0 {
pipBinary = filepath.Join(virtualEnv, "bin", pipBinary)
}
flags := PipInstallFlags
// flags := append([]string{"-m", "pip"}, PipInstallFlags...)
if len(extraArgs) > 0 {
flags = append(flags, extraArgs...)
}
if len(version) > 0 {
module = fmt.Sprintf("%s==%s", module, version)
}
if len(module) > 0 {
flags = append(flags, module)
}
if err := executeFn(pipBinary, flags...); err != nil {
return fmt.Errorf("failed to install %s: %w", module, err)
}
return nil
}
func InstallRequirements(
executeFn func(executable string, params ...string) error,
virtualEnv string,
requirementsFile string,
) error {
log.Entry().Debug("installing requirements")
return Install(executeFn, virtualEnv, "", "", []string{"--requirement", requirementsFile})
}
func InstallWheel(
executeFn func(executable string, params ...string) error,
virtualEnv string,
) error {
log.Entry().Debug("installing wheel")
return Install(executeFn, virtualEnv, "wheel", "", nil)
}
func InstallTwine(
executeFn func(executable string, params ...string) error,
virtualEnv string,
) error {
log.Entry().Debug("installing twine")
return Install(executeFn, virtualEnv, "twine", "", nil)
}
func InstallCycloneDX(
executeFn func(executable string, params ...string) error,
virtualEnv string,
cycloneDXVersion string,
) error {
log.Entry().Debug("installing cyclonedx-bom")
return Install(executeFn, virtualEnv, "cyclonedx-bom", cycloneDXVersion, nil)
}

82
pkg/python/pip_test.go Normal file
View File

@@ -0,0 +1,82 @@
//go:build unit
// +build unit
package python
import (
"testing"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/stretchr/testify/assert"
)
func TestInstallRequirements(t *testing.T) {
// init
mockRunner := mock.ExecMockRunner{}
// test
err := InstallRequirements(mockRunner.RunExecutable, "", "requirements.txt")
// assert
assert.NoError(t, err)
assert.Equal(t, "pip", mockRunner.Calls[0].Exec)
assert.Equal(t, []string{
"install",
"--upgrade",
"--root-user-action=ignore",
"--requirement", "requirements.txt"}, mockRunner.Calls[0].Params)
}
func TestInstallWheel(t *testing.T) {
// init
mockRunner := mock.ExecMockRunner{}
// test
err := InstallWheel(mockRunner.RunExecutable, "")
// assert
assert.NoError(t, err)
assert.Len(t, mockRunner.Calls, 1)
assert.Equal(t, "pip", mockRunner.Calls[0].Exec)
assert.Equal(t, []string{
"install",
"--upgrade",
"--root-user-action=ignore",
"wheel"}, mockRunner.Calls[0].Params)
}
func TestInstallTwine(t *testing.T) {
// init
mockRunner := mock.ExecMockRunner{}
// test
err := InstallTwine(mockRunner.RunExecutable, "")
// assert
assert.NoError(t, err)
assert.Len(t, mockRunner.Calls, 1)
assert.Equal(t, "pip", mockRunner.Calls[0].Exec)
assert.Equal(t, []string{
"install",
"--upgrade",
"--root-user-action=ignore",
"twine"}, mockRunner.Calls[0].Params)
}
func TestInstallCycloneDXWithVersion(t *testing.T) {
// init
mockRunner := mock.ExecMockRunner{}
// test
err := InstallCycloneDX(mockRunner.RunExecutable, "", "1.0.0")
// assert
assert.NoError(t, err)
assert.Len(t, mockRunner.Calls, 1)
assert.Equal(t, "pip", mockRunner.Calls[0].Exec)
assert.Equal(t, []string{
"install",
"--upgrade",
"--root-user-action=ignore",
"cyclonedx-bom==1.0.0"}, mockRunner.Calls[0].Params)
}

33
pkg/python/publish.go Normal file
View File

@@ -0,0 +1,33 @@
package python
import (
"path/filepath"
)
func PublishPackage(
executeFn func(executable string, params ...string) error,
virtualEnv string,
repository string,
username string,
password string,
) error {
// install dependency
if err := InstallTwine(executeFn, virtualEnv); err != nil {
return err
}
// handle virtual environment
twineBinary := "twine"
if len(virtualEnv) > 0 {
twineBinary = filepath.Join(virtualEnv, "bin", twineBinary)
}
// publish project
return executeFn(
twineBinary,
"upload",
"--username", username,
"--password", password,
"--repository-url", repository,
"--disable-progress-bar",
"dist/*",
)
}

View File

@@ -0,0 +1,36 @@
//go:build unit
// +build unit
package python
import (
"testing"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/stretchr/testify/assert"
)
func TestPublishWithVirtualEnv(t *testing.T) {
// init
mockRunner := mock.ExecMockRunner{}
// test
err := PublishPackage(mockRunner.RunExecutable, ".venv", "repository", "anything", "anything")
// assert
assert.NoError(t, err)
assert.Equal(t, ".venv/bin/pip", mockRunner.Calls[0].Exec)
assert.Equal(t, []string{
"install",
"--upgrade",
"--root-user-action=ignore",
"twine"}, mockRunner.Calls[0].Params)
assert.Equal(t, ".venv/bin/twine", mockRunner.Calls[1].Exec)
assert.Equal(t, []string{
"upload",
"--username", "anything",
"--password", "anything",
"--repository-url", "repository",
"--disable-progress-bar",
"dist/*"}, mockRunner.Calls[1].Params)
}