1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-01-18 05:18:24 +02:00

feat(artifactPrepareVersion): helm & propagate version (#3627)

* feat(artifactPrepareVersion): helm & propagate version

* chore: small refactoring

* chore: fix linting issue

* fix version persistence
This commit is contained in:
Oliver Nocon 2022-03-15 09:08:24 +01:00 committed by GitHub
parent 6651eaf6c8
commit 7ec512cb9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 630 additions and 44 deletions

View File

@ -124,11 +124,9 @@ func runArtifactPrepareVersion(config *artifactPrepareVersionOptions, telemetryD
}
}
versioningType := config.VersioningType
// support former groovy versioning template and translate into new options
if len(config.VersioningTemplate) > 0 {
versioningType, _, config.IncludeCommitID = templateCompatibility(config.VersioningTemplate)
config.VersioningType, _, config.IncludeCommitID = templateCompatibility(config.VersioningTemplate)
}
version, err := artifact.GetVersion()
@ -148,18 +146,12 @@ func runArtifactPrepareVersion(config *artifactPrepareVersionOptions, telemetryD
commonPipelineEnvironment.git.headCommitID = gitCommitID
newVersion := version
if versioningType == "cloud" || versioningType == "cloud_noTag" {
versioningTempl, err := versioningTemplate(artifact.VersioningScheme())
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return errors.Wrapf(err, "failed to get versioning template for scheme '%v'", artifact.VersioningScheme())
}
if config.VersioningType == "cloud" || config.VersioningType == "cloud_noTag" {
now := time.Now()
newVersion, err = calculateNewVersion(versioningTempl, version, gitCommitID, config.IncludeCommitID, config.ShortCommitID, config.UnixTimestamp, now)
newVersion, err = calculateCloudVersion(artifact, config, version, gitCommitID, now)
if err != nil {
return errors.Wrap(err, "failed to calculate new version")
return err
}
worktree, err := getWorktree(repository)
@ -185,9 +177,15 @@ func runArtifactPrepareVersion(config *artifactPrepareVersionOptions, telemetryD
}
}
//ToDo: what about closure in current Groovy step. Discard the possibility or provide extension mechanism?
// propagate version information to additional descriptors
if len(config.AdditionalTargetTools) > 0 {
err = propagateVersion(config, utils, &artifactOpts, newVersion, gitCommitID, now)
if err != nil {
return err
}
}
if versioningType == "cloud" {
if config.VersioningType == "cloud" {
// commit changes and push to repository (including new version tag)
gitCommitID, err = pushChanges(config, newVersion, repository, worktree, now)
if err != nil {
@ -450,3 +448,71 @@ func templateCompatibility(groovyTemplate string) (versioningType string, useTim
return
}
func calculateCloudVersion(artifact versioning.Artifact, config *artifactPrepareVersionOptions, version, gitCommitID string, timestamp time.Time) (string, error) {
versioningTempl, err := versioningTemplate(artifact.VersioningScheme())
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return "", errors.Wrapf(err, "failed to get versioning template for scheme '%v'", artifact.VersioningScheme())
}
newVersion, err := calculateNewVersion(versioningTempl, version, gitCommitID, config.IncludeCommitID, config.ShortCommitID, config.UnixTimestamp, timestamp)
if err != nil {
return "", errors.Wrap(err, "failed to calculate new version")
}
return newVersion, nil
}
func propagateVersion(config *artifactPrepareVersionOptions, utils artifactPrepareVersionUtils, artifactOpts *versioning.Options, newVersion, gitCommitID string, now time.Time) error {
var err error
if len(config.AdditionalTargetDescriptors) > 0 && len(config.AdditionalTargetTools) != len(config.AdditionalTargetDescriptors) {
log.SetErrorCategory(log.ErrorConfiguration)
return fmt.Errorf("additionalTargetDescriptors cannot have a different number of entries than additionalTargetTools")
}
for i, targetTool := range config.AdditionalTargetTools {
if targetTool == config.BuildTool {
// ignore configured build tool
continue
}
var buildDescriptors []string
if len(config.AdditionalTargetDescriptors) > 0 {
buildDescriptors, err = utils.Glob(config.AdditionalTargetDescriptors[i])
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return fmt.Errorf("failed to retrieve build descriptors: %w", err)
}
}
if len(buildDescriptors) == 0 {
buildDescriptors = append(buildDescriptors, "")
}
// in case of helm, make sure that app version is adapted as well
artifactOpts.HelmUpdateAppVersion = true
for _, buildDescriptor := range buildDescriptors {
targetArtifact, err := versioning.GetArtifact(targetTool, buildDescriptor, artifactOpts, utils)
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return fmt.Errorf("failed to retrieve artifact: %w", err)
}
// Make sure that version type fits to target artifact
var descriptorVersion string
if config.VersioningType == "cloud" || config.VersioningType == "cloud_noTag" {
descriptorVersion, err = calculateCloudVersion(targetArtifact, config, newVersion, gitCommitID, now)
if err != nil {
return err
}
}
err = targetArtifact.SetVersion(descriptorVersion)
if err != nil {
return fmt.Errorf("failed to set additional target version for '%v': %w", targetTool, err)
}
}
}
return nil
}

View File

@ -18,25 +18,27 @@ import (
)
type artifactPrepareVersionOptions struct {
BuildTool string `json:"buildTool,omitempty" validate:"possible-values=custom docker dub golang gradle maven mta npm pip sbt yarn"`
CommitUserName string `json:"commitUserName,omitempty"`
CustomVersionField string `json:"customVersionField,omitempty"`
CustomVersionSection string `json:"customVersionSection,omitempty"`
CustomVersioningScheme string `json:"customVersioningScheme,omitempty" validate:"possible-values=docker maven pep440 semver2"`
DockerVersionSource string `json:"dockerVersionSource,omitempty"`
FetchCoordinates bool `json:"fetchCoordinates,omitempty"`
FilePath string `json:"filePath,omitempty"`
GlobalSettingsFile string `json:"globalSettingsFile,omitempty"`
IncludeCommitID bool `json:"includeCommitId,omitempty"`
M2Path string `json:"m2Path,omitempty"`
Password string `json:"password,omitempty"`
ProjectSettingsFile string `json:"projectSettingsFile,omitempty"`
ShortCommitID bool `json:"shortCommitId,omitempty"`
TagPrefix string `json:"tagPrefix,omitempty"`
UnixTimestamp bool `json:"unixTimestamp,omitempty"`
Username string `json:"username,omitempty"`
VersioningTemplate string `json:"versioningTemplate,omitempty"`
VersioningType string `json:"versioningType,omitempty" validate:"possible-values=cloud cloud_noTag library"`
AdditionalTargetTools []string `json:"additionalTargetTools,omitempty" validate:"possible-values=custom docker dub golang gradle helm maven mta npm pip sbt yarn"`
AdditionalTargetDescriptors []string `json:"additionalTargetDescriptors,omitempty"`
BuildTool string `json:"buildTool,omitempty" validate:"possible-values=custom docker dub golang gradle helm maven mta npm pip sbt yarn"`
CommitUserName string `json:"commitUserName,omitempty"`
CustomVersionField string `json:"customVersionField,omitempty"`
CustomVersionSection string `json:"customVersionSection,omitempty"`
CustomVersioningScheme string `json:"customVersioningScheme,omitempty" validate:"possible-values=docker maven pep440 semver2"`
DockerVersionSource string `json:"dockerVersionSource,omitempty"`
FetchCoordinates bool `json:"fetchCoordinates,omitempty"`
FilePath string `json:"filePath,omitempty"`
GlobalSettingsFile string `json:"globalSettingsFile,omitempty"`
IncludeCommitID bool `json:"includeCommitId,omitempty"`
M2Path string `json:"m2Path,omitempty"`
Password string `json:"password,omitempty"`
ProjectSettingsFile string `json:"projectSettingsFile,omitempty"`
ShortCommitID bool `json:"shortCommitId,omitempty"`
TagPrefix string `json:"tagPrefix,omitempty"`
UnixTimestamp bool `json:"unixTimestamp,omitempty"`
Username string `json:"username,omitempty"`
VersioningTemplate string `json:"versioningTemplate,omitempty"`
VersioningType string `json:"versioningType,omitempty" validate:"possible-values=cloud cloud_noTag library"`
}
type artifactPrepareVersionCommonPipelineEnvironment struct {
@ -236,6 +238,8 @@ Define ` + "`" + `buildTool: custom` + "`" + `, ` + "`" + `filePath: <path to yo
}
func addArtifactPrepareVersionFlags(cmd *cobra.Command, stepConfig *artifactPrepareVersionOptions) {
cmd.Flags().StringSliceVar(&stepConfig.AdditionalTargetTools, "additionalTargetTools", []string{}, "Additional buildTool targets where descriptors need to be updated besides the main `buildTool`.")
cmd.Flags().StringSliceVar(&stepConfig.AdditionalTargetDescriptors, "additionalTargetDescriptors", []string{}, "Defines patterns for build descriptors which should be used for option [`additionalTargetTools`](additionaltargettools).")
cmd.Flags().StringVar(&stepConfig.BuildTool, "buildTool", os.Getenv("PIPER_buildTool"), "Defines the tool which is used for building the artifact.")
cmd.Flags().StringVar(&stepConfig.CommitUserName, "commitUserName", `Project Piper`, "Defines the user name which appears in version control for the versioning update (in case `versioningType: cloud`).")
cmd.Flags().StringVar(&stepConfig.CustomVersionField, "customVersionField", os.Getenv("PIPER_customVersionField"), "For `buildTool: custom`: Defines the field which contains the version in the descriptor file.")
@ -274,6 +278,24 @@ func artifactPrepareVersionMetadata() config.StepData {
{Name: "gitSshKeyCredentialsId", Description: "Jenkins 'SSH Username with private key' credentials ID ssh key for accessing your git repository. You can find details about how to generate an ssh key in the [GitHub documentation](https://docs.github.com/en/enterprise/2.15/user/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent).", Type: "jenkins", Aliases: []config.Alias{{Name: "gitCredentialsId", Deprecated: true}}},
},
Parameters: []config.StepParameters{
{
Name: "additionalTargetTools",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "[]string",
Mandatory: false,
Aliases: []config.Alias{},
Default: []string{},
},
{
Name: "additionalTargetDescriptors",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "[]string",
Mandatory: false,
Aliases: []config.Alias{},
Default: []string{},
},
{
Name: "buildTool",
ResourceRef: []config.ResourceReference{},

View File

@ -2,19 +2,23 @@ package cmd
import (
"fmt"
"net/http"
"testing"
"time"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/SAP/jenkins-library/pkg/versioning"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/ghodss/yaml"
"github.com/stretchr/testify/assert"
"helm.sh/helm/v3/pkg/chart"
"github.com/go-git/go-git/v5"
gitConfig "github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport/http"
gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
)
@ -166,6 +170,24 @@ func (w *gitWorktreeMock) Commit(msg string, opts *git.CommitOptions) (plumbing.
return w.commitHash, nil
}
type artifactPrepareVersionMockUtils struct {
*mock.ExecMockRunner
*mock.FilesMock
}
func newArtifactPrepareVersionMockUtils() *artifactPrepareVersionMockUtils {
utils := artifactPrepareVersionMockUtils{
ExecMockRunner: &mock.ExecMockRunner{},
FilesMock: &mock.FilesMock{},
}
return &utils
}
func (a *artifactPrepareVersionMockUtils) DownloadFile(url, filename string, header http.Header, cookies []*http.Cookie) error {
// so far no dedicated logic required for testing
return nil
}
func TestRunArtifactPrepareVersion(t *testing.T) {
t.Run("success case - cloud", func(t *testing.T) {
@ -579,7 +601,7 @@ func TestPushChanges(t *testing.T) {
assert.Equal(t, &git.CommitOptions{All: true, Author: &object.Signature{Name: "Project Piper", When: testTime}}, worktree.commitOpts)
assert.Equal(t, "1.2.3", repo.tag)
assert.Equal(t, "428ecf70bc22df0ba3dcf194b5ce53e769abab07", repo.tagHash.String())
assert.Equal(t, &git.PushOptions{RefSpecs: []gitConfig.RefSpec{"refs/tags/1.2.3:refs/tags/1.2.3"}, Auth: &http.BasicAuth{Username: config.Username, Password: config.Password}}, repo.pushOptions)
assert.Equal(t, &git.PushOptions{RefSpecs: []gitConfig.RefSpec{"refs/tags/1.2.3:refs/tags/1.2.3"}, Auth: &gitHttp.BasicAuth{Username: config.Username, Password: config.Password}}, repo.pushOptions)
})
t.Run("success - ssh fallback", func(t *testing.T) {
@ -724,3 +746,79 @@ func TestConvertHTTPToSSHURL(t *testing.T) {
assert.Equal(t, test.expected, convertHTTPToSSHURL(test.httpURL))
}
}
func TestPropagateVersion(t *testing.T) {
t.Parallel()
gitCommitID := "theGitCommitId"
testTime := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) //20200101000000
t.Run("success case", func(t *testing.T) {
config := artifactPrepareVersionOptions{
VersioningType: "cloud",
AdditionalTargetTools: []string{"helm"},
}
chartMetadata := chart.Metadata{Version: "1.2.3"}
content, err := yaml.Marshal(chartMetadata)
assert.NoError(t, err)
utils := newArtifactPrepareVersionMockUtils()
utils.AddFile("myChart/Chart.yaml", content)
artifactOpts := versioning.Options{}
err = propagateVersion(&config, utils, &artifactOpts, "1.2.4", gitCommitID, testTime)
assert.NoError(t, err)
})
t.Run("success case - dedicated build descriptors", func(t *testing.T) {
config := artifactPrepareVersionOptions{
VersioningType: "cloud",
AdditionalTargetTools: []string{"helm"},
AdditionalTargetDescriptors: []string{"myChart/Chart.yaml"},
}
chartMetadata := chart.Metadata{Version: "1.2.3"}
content, err := yaml.Marshal(chartMetadata)
assert.NoError(t, err)
utils := newArtifactPrepareVersionMockUtils()
utils.AddFile("myChart/Chart.yaml", content)
artifactOpts := versioning.Options{}
err = propagateVersion(&config, utils, &artifactOpts, "1.2.4", gitCommitID, testTime)
assert.NoError(t, err)
})
t.Run("success case - noop", func(t *testing.T) {
config := artifactPrepareVersionOptions{}
utils := newArtifactPrepareVersionMockUtils()
artifactOpts := versioning.Options{}
err := propagateVersion(&config, utils, &artifactOpts, "1.2.4", gitCommitID, testTime)
assert.NoError(t, err)
})
t.Run("error case - wrong config", func(t *testing.T) {
config := artifactPrepareVersionOptions{
AdditionalTargetDescriptors: []string{"pom.xml"},
AdditionalTargetTools: []string{"maven", "helm"},
}
utils := newArtifactPrepareVersionMockUtils()
artifactOpts := versioning.Options{}
err := propagateVersion(&config, utils, &artifactOpts, "1.2.4", gitCommitID, testTime)
assert.EqualError(t, err, "additionalTargetDescriptors cannot have a different number of entries than additionalTargetTools")
})
t.Run("error case - wrong target tool", func(t *testing.T) {
config := artifactPrepareVersionOptions{
AdditionalTargetTools: []string{"notKnown"},
}
utils := newArtifactPrepareVersionMockUtils()
artifactOpts := versioning.Options{}
err := propagateVersion(&config, utils, &artifactOpts, "1.2.4", gitCommitID, testTime)
assert.Contains(t, fmt.Sprint(err), "failed to retrieve artifact")
})
}

96
pkg/versioning/helm.go Normal file
View File

@ -0,0 +1,96 @@
package versioning
import (
"fmt"
"github.com/ghodss/yaml"
"helm.sh/helm/v3/pkg/chart"
)
// JSONfile defines an artifact using a json file for versioning
type HelmChart struct {
path string
metadata chart.Metadata
utils Utils
updateAppVersion bool
}
func (h *HelmChart) init() error {
if h.utils == nil {
return fmt.Errorf("no file utils provided")
}
if len(h.path) == 0 {
charts, err := h.utils.Glob("**/Chart.yaml")
if len(charts) == 0 || err != nil {
return fmt.Errorf("failed to find a helm chart file")
}
// use first chart which can be found
h.path = charts[0]
}
if len(h.metadata.Version) == 0 {
content, err := h.utils.FileRead(h.path)
if err != nil {
return fmt.Errorf("failed to read file '%v': %w", h.path, err)
}
err = yaml.Unmarshal(content, &h.metadata)
if err != nil {
return fmt.Errorf("helm chart content invalid '%v': %w", h.path, err)
}
}
return nil
}
// VersioningScheme returns the relevant versioning scheme
func (h *HelmChart) VersioningScheme() string {
return "semver2"
}
// GetVersion returns the current version of the artifact with a JSON-based build descriptor
func (h *HelmChart) GetVersion() (string, error) {
if err := h.init(); err != nil {
return "", fmt.Errorf("failed to init helm chart versioning: %w", err)
}
return h.metadata.Version, nil
}
// SetVersion updates the version of the artifact with a JSON-based build descriptor
func (h *HelmChart) SetVersion(version string) error {
if err := h.init(); err != nil {
return fmt.Errorf("failed to init helm chart versioning: %w", err)
}
h.metadata.Version = version
if h.updateAppVersion {
h.metadata.AppVersion = version
}
content, err := yaml.Marshal(h.metadata)
if err != nil {
return fmt.Errorf("failed to create chart content for '%v': %w", h.path, err)
}
err = h.utils.FileWrite(h.path, content, 666)
if err != nil {
return fmt.Errorf("failed to write file '%v': %w", h.path, err)
}
return nil
}
// GetCoordinates returns the coordinates
func (h *HelmChart) GetCoordinates() (Coordinates, error) {
result := Coordinates{}
projectVersion, err := h.GetVersion()
if err != nil {
return result, err
}
result.ArtifactID = h.metadata.Name
result.Version = projectVersion
result.GroupID = h.metadata.Home
return result, nil
}

218
pkg/versioning/helm_test.go Normal file
View File

@ -0,0 +1,218 @@
package versioning
import (
"fmt"
"testing"
"github.com/ghodss/yaml"
"github.com/stretchr/testify/assert"
"helm.sh/helm/v3/pkg/chart"
)
func TestHelmChartInit(t *testing.T) {
t.Run("success case", func(t *testing.T) {
chartMetadata := chart.Metadata{Version: "1.2.3"}
content, err := yaml.Marshal(chartMetadata)
assert.NoError(t, err)
fileUtils := newVersioningMockUtils()
fileUtils.AddFile("testchart/Chart.yaml", content)
helmChart := HelmChart{
utils: fileUtils,
}
err = helmChart.init()
assert.NoError(t, err)
assert.Equal(t, "1.2.3", helmChart.metadata.Version)
})
t.Run("success case - with chart path", func(t *testing.T) {
chartMetadata := chart.Metadata{Version: "1.2.3"}
content, err := yaml.Marshal(chartMetadata)
assert.NoError(t, err)
fileUtils := newVersioningMockUtils()
fileUtils.AddFile("chart1/Chart.yaml", []byte(""))
fileUtils.AddFile("chart2/Chart.yaml", content)
fileUtils.AddFile("chart3/Chart.yaml", []byte(""))
helmChart := HelmChart{
path: "chart2/Chart.yaml",
utils: fileUtils,
}
err = helmChart.init()
assert.NoError(t, err)
assert.Equal(t, "1.2.3", helmChart.metadata.Version)
})
t.Run("error case - init failed with missing utils", func(t *testing.T) {
helmChart := HelmChart{
path: "chart2/Chart.yaml",
}
err := helmChart.init()
assert.EqualError(t, err, "no file utils provided")
})
t.Run("error case - init failed with missing chart", func(t *testing.T) {
fileUtils := newVersioningMockUtils()
helmChart := HelmChart{
utils: fileUtils,
}
err := helmChart.init()
assert.EqualError(t, err, "failed to find a helm chart file")
})
t.Run("error case - failed reading file", func(t *testing.T) {
fileUtils := newVersioningMockUtils()
fileUtils.FileReadErrors = map[string]error{"testchart/Chart.yaml": fmt.Errorf("read error")}
helmChart := HelmChart{
utils: fileUtils,
path: "testchart/Chart.yaml",
}
err := helmChart.init()
assert.EqualError(t, err, "failed to read file 'testchart/Chart.yaml': read error")
})
t.Run("error case - chart invalid", func(t *testing.T) {
fileUtils := newVersioningMockUtils()
fileUtils.AddFile("testchart/Chart.yaml", []byte("{"))
helmChart := HelmChart{
utils: fileUtils,
path: "testchart/Chart.yaml",
}
err := helmChart.init()
assert.Contains(t, fmt.Sprint(err), "helm chart content invalid 'testchart/Chart.yaml'")
})
}
func TestHelmChartVersioningScheme(t *testing.T) {
helmChart := HelmChart{}
assert.Equal(t, "semver2", helmChart.VersioningScheme())
}
func TestHelmChartGetVersion(t *testing.T) {
t.Run("success case", func(t *testing.T) {
chartMetadata := chart.Metadata{Version: "1.2.3"}
content, err := yaml.Marshal(chartMetadata)
assert.NoError(t, err)
fileUtils := newVersioningMockUtils()
fileUtils.AddFile("testchart/Chart.yaml", content)
helmChart := HelmChart{
utils: fileUtils,
}
version, err := helmChart.GetVersion()
assert.NoError(t, err)
assert.Equal(t, "1.2.3", version)
})
t.Run("error case - init failed", func(t *testing.T) {
fileUtils := newVersioningMockUtils()
helmChart := HelmChart{
utils: fileUtils,
}
_, err := helmChart.GetVersion()
assert.Contains(t, fmt.Sprint(err), "failed to init helm chart versioning:")
})
}
func TestHelmChartSetVersion(t *testing.T) {
t.Run("success case", func(t *testing.T) {
fileUtils := newVersioningMockUtils()
helmChart := HelmChart{
utils: fileUtils,
path: "testchart/Chart.yaml",
metadata: chart.Metadata{Version: "1.2.3"},
}
err := helmChart.SetVersion("1.2.4")
assert.NoError(t, err)
assert.Equal(t, "1.2.4", helmChart.metadata.Version)
fileContent, err := fileUtils.FileRead("testchart/Chart.yaml")
assert.NoError(t, err)
assert.Contains(t, string(fileContent), "version: 1.2.4")
})
t.Run("success case - update app version", func(t *testing.T) {
fileUtils := newVersioningMockUtils()
helmChart := HelmChart{
utils: fileUtils,
path: "testchart/Chart.yaml",
metadata: chart.Metadata{Version: "1.2.3"},
updateAppVersion: true,
}
err := helmChart.SetVersion("1.2.4")
assert.NoError(t, err)
assert.Equal(t, "1.2.4", helmChart.metadata.AppVersion)
})
t.Run("error case - init failed with missing chart", func(t *testing.T) {
fileUtils := newVersioningMockUtils()
helmChart := HelmChart{
utils: fileUtils,
}
err := helmChart.SetVersion("1.2.4")
assert.Contains(t, fmt.Sprint(err), "failed to init helm chart versioning:")
})
t.Run("error case - failed to write chart", func(t *testing.T) {
fileUtils := newVersioningMockUtils()
fileUtils.FileWriteError = fmt.Errorf("write error")
helmChart := HelmChart{
path: "testchart/Chart.yaml",
utils: fileUtils,
metadata: chart.Metadata{Version: "1.2.3"},
}
err := helmChart.SetVersion("1.2.4")
assert.EqualError(t, err, "failed to write file 'testchart/Chart.yaml': write error")
})
}
func TestHelmChartGetCoordinates(t *testing.T) {
t.Run("success case", func(t *testing.T) {
fileUtils := newVersioningMockUtils()
helmChart := HelmChart{
utils: fileUtils,
path: "testchart/Chart.yaml",
metadata: chart.Metadata{Version: "1.2.3", Name: "myChart", Home: "myHome"},
}
coordinates, err := helmChart.GetCoordinates()
assert.NoError(t, err)
assert.Equal(t, Coordinates{GroupID: "myHome", ArtifactID: "myChart", Version: "1.2.3"}, coordinates)
})
t.Run("error case - version retrieval failed", func(t *testing.T) {
fileUtils := newVersioningMockUtils()
helmChart := HelmChart{
utils: fileUtils,
}
_, err := helmChart.GetCoordinates()
assert.Contains(t, fmt.Sprint(err), "failed to init helm chart versioning:")
})
}

View File

@ -27,14 +27,15 @@ type Artifact interface {
// Options define build tool specific settings in order to properly retrieve e.g. the version / coordinates of an artifact
type Options struct {
ProjectSettingsFile string
DockerImage string
GlobalSettingsFile string
M2Path string
VersionSource string
VersionSection string
VersionField string
VersioningScheme string
ProjectSettingsFile string
DockerImage string
GlobalSettingsFile string
M2Path string
VersionSource string
VersionSection string
VersionField string
VersioningScheme string
HelmUpdateAppVersion bool
}
// Utils defines the versioning operations for various build tools
@ -106,6 +107,12 @@ func GetArtifact(buildTool, buildDescriptorFilePath string, opts *Options, utils
default:
artifact = &Versionfile{path: buildDescriptorFilePath}
}
case "helm":
artifact = &HelmChart{
path: buildDescriptorFilePath,
utils: utils,
updateAppVersion: opts.HelmUpdateAppVersion,
}
case "maven":
if len(buildDescriptorFilePath) == 0 {
buildDescriptorFilePath = "pom.xml"

View File

@ -1,11 +1,31 @@
package versioning
import (
"net/http"
"testing"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/stretchr/testify/assert"
)
type versioningMockUtils struct {
*mock.ExecMockRunner
*mock.FilesMock
}
func newVersioningMockUtils() *versioningMockUtils {
utils := versioningMockUtils{
ExecMockRunner: &mock.ExecMockRunner{},
FilesMock: &mock.FilesMock{},
}
return &utils
}
func (v *versioningMockUtils) DownloadFile(url, filename string, header http.Header, cookies []*http.Cookie) error {
// so far no dedicated logic required for testing
return nil
}
func TestGetArtifact(t *testing.T) {
t.Run("custom", func(t *testing.T) {
custom, err := GetArtifact("custom", "test.ini", &Options{VersionField: "theversion", VersionSection: "test"}, nil)
@ -76,6 +96,17 @@ func TestGetArtifact(t *testing.T) {
assert.Equal(t, "semver2", gradle.VersioningScheme())
})
t.Run("helm", func(t *testing.T) {
helm, err := GetArtifact("helm", "testchart/Chart.yaml", &Options{}, nil)
assert.NoError(t, err)
theType, ok := helm.(*HelmChart)
assert.True(t, ok)
assert.Equal(t, "testchart/Chart.yaml", theType.path)
assert.Equal(t, "semver2", helm.VersioningScheme())
})
t.Run("maven", func(t *testing.T) {
opts := Options{
ProjectSettingsFile: "projectsettings.xml",

View File

@ -82,6 +82,53 @@ spec:
- name: gitCredentialsId
deprecated: true
params:
- name: additionalTargetTools
type: "[]string"
description: Additional buildTool targets where descriptors need to be updated besides the main `buildTool`.
longDescription: |
**Only for versioning types `cloud` and `cloud_noTag`.** This parameter allows you to propagate the version to other build-tool specific descriptors.
If the parameter [`additionalTargetDescriptors`](#additionaltargetdescriptors) is not defined the default build descriptors are used.
One example is to propagate the version into a helm chart.
This can be achieved like
```
steps:
artifactPrepareVersion:
additionalTargetTools:
- helm
```
scope:
- PARAMETERS
- STAGES
- STEPS
possibleValues:
- custom
- docker
- dub
- golang
- gradle
- helm
- maven
- mta
- npm
- pip
- sbt
- yarn
- name: additionalTargetDescriptors
type: "[]string"
description: Defines patterns for build descriptors which should be used for option [`additionalTargetTools`](additionaltargettools).
longDescription: |
**Only for versioning types `cloud` and `cloud_noTag`.** In case default build descriptors cannot be used for [`additionalTargetTools`](additionaltargettools) this parameter allows to define a dedicated search pattern per build tool.
For each entry in [`additionalTargetTools`](additionaltargettools) a dedicated entry has to be maintained.
You can use either a file name or a glob pattern like `**/package.json`.
For `helm` the default value is `**/Chart.yaml`, thus typically no adaptions are required.
scope:
- PARAMETERS
- STAGES
- STEPS
- name: buildTool
type: string
description: Defines the tool which is used for building the artifact.
@ -97,6 +144,7 @@ spec:
- dub
- golang
- gradle
- helm
- maven
- mta
- npm