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:
parent
6651eaf6c8
commit
7ec512cb9f
@ -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
|
||||
}
|
||||
|
@ -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{},
|
||||
|
@ -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
96
pkg/versioning/helm.go
Normal 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
218
pkg/versioning/helm_test.go
Normal 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:")
|
||||
})
|
||||
}
|
@ -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"
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user