mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-01-30 05:59:39 +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
|
// support former groovy versioning template and translate into new options
|
||||||
if len(config.VersioningTemplate) > 0 {
|
if len(config.VersioningTemplate) > 0 {
|
||||||
versioningType, _, config.IncludeCommitID = templateCompatibility(config.VersioningTemplate)
|
config.VersioningType, _, config.IncludeCommitID = templateCompatibility(config.VersioningTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
version, err := artifact.GetVersion()
|
version, err := artifact.GetVersion()
|
||||||
@ -148,18 +146,12 @@ func runArtifactPrepareVersion(config *artifactPrepareVersionOptions, telemetryD
|
|||||||
commonPipelineEnvironment.git.headCommitID = gitCommitID
|
commonPipelineEnvironment.git.headCommitID = gitCommitID
|
||||||
newVersion := version
|
newVersion := version
|
||||||
|
|
||||||
if versioningType == "cloud" || versioningType == "cloud_noTag" {
|
if config.VersioningType == "cloud" || config.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())
|
|
||||||
}
|
|
||||||
|
|
||||||
now := time.Now()
|
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 {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to calculate new version")
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
worktree, err := getWorktree(repository)
|
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)
|
// commit changes and push to repository (including new version tag)
|
||||||
gitCommitID, err = pushChanges(config, newVersion, repository, worktree, now)
|
gitCommitID, err = pushChanges(config, newVersion, repository, worktree, now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -450,3 +448,71 @@ func templateCompatibility(groovyTemplate string) (versioningType string, useTim
|
|||||||
|
|
||||||
return
|
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 {
|
type artifactPrepareVersionOptions struct {
|
||||||
BuildTool string `json:"buildTool,omitempty" validate:"possible-values=custom docker dub golang gradle maven mta npm pip sbt yarn"`
|
AdditionalTargetTools []string `json:"additionalTargetTools,omitempty" validate:"possible-values=custom docker dub golang gradle helm maven mta npm pip sbt yarn"`
|
||||||
CommitUserName string `json:"commitUserName,omitempty"`
|
AdditionalTargetDescriptors []string `json:"additionalTargetDescriptors,omitempty"`
|
||||||
CustomVersionField string `json:"customVersionField,omitempty"`
|
BuildTool string `json:"buildTool,omitempty" validate:"possible-values=custom docker dub golang gradle helm maven mta npm pip sbt yarn"`
|
||||||
CustomVersionSection string `json:"customVersionSection,omitempty"`
|
CommitUserName string `json:"commitUserName,omitempty"`
|
||||||
CustomVersioningScheme string `json:"customVersioningScheme,omitempty" validate:"possible-values=docker maven pep440 semver2"`
|
CustomVersionField string `json:"customVersionField,omitempty"`
|
||||||
DockerVersionSource string `json:"dockerVersionSource,omitempty"`
|
CustomVersionSection string `json:"customVersionSection,omitempty"`
|
||||||
FetchCoordinates bool `json:"fetchCoordinates,omitempty"`
|
CustomVersioningScheme string `json:"customVersioningScheme,omitempty" validate:"possible-values=docker maven pep440 semver2"`
|
||||||
FilePath string `json:"filePath,omitempty"`
|
DockerVersionSource string `json:"dockerVersionSource,omitempty"`
|
||||||
GlobalSettingsFile string `json:"globalSettingsFile,omitempty"`
|
FetchCoordinates bool `json:"fetchCoordinates,omitempty"`
|
||||||
IncludeCommitID bool `json:"includeCommitId,omitempty"`
|
FilePath string `json:"filePath,omitempty"`
|
||||||
M2Path string `json:"m2Path,omitempty"`
|
GlobalSettingsFile string `json:"globalSettingsFile,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
IncludeCommitID bool `json:"includeCommitId,omitempty"`
|
||||||
ProjectSettingsFile string `json:"projectSettingsFile,omitempty"`
|
M2Path string `json:"m2Path,omitempty"`
|
||||||
ShortCommitID bool `json:"shortCommitId,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
TagPrefix string `json:"tagPrefix,omitempty"`
|
ProjectSettingsFile string `json:"projectSettingsFile,omitempty"`
|
||||||
UnixTimestamp bool `json:"unixTimestamp,omitempty"`
|
ShortCommitID bool `json:"shortCommitId,omitempty"`
|
||||||
Username string `json:"username,omitempty"`
|
TagPrefix string `json:"tagPrefix,omitempty"`
|
||||||
VersioningTemplate string `json:"versioningTemplate,omitempty"`
|
UnixTimestamp bool `json:"unixTimestamp,omitempty"`
|
||||||
VersioningType string `json:"versioningType,omitempty" validate:"possible-values=cloud cloud_noTag library"`
|
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 {
|
type artifactPrepareVersionCommonPipelineEnvironment struct {
|
||||||
@ -236,6 +238,8 @@ Define ` + "`" + `buildTool: custom` + "`" + `, ` + "`" + `filePath: <path to yo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func addArtifactPrepareVersionFlags(cmd *cobra.Command, stepConfig *artifactPrepareVersionOptions) {
|
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.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.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.")
|
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}}},
|
{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{
|
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",
|
Name: "buildTool",
|
||||||
ResourceRef: []config.ResourceReference{},
|
ResourceRef: []config.ResourceReference{},
|
||||||
|
@ -2,19 +2,23 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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/versioning"
|
||||||
|
|
||||||
"github.com/SAP/jenkins-library/pkg/telemetry"
|
"github.com/ghodss/yaml"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"helm.sh/helm/v3/pkg/chart"
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
gitConfig "github.com/go-git/go-git/v5/config"
|
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"
|
||||||
"github.com/go-git/go-git/v5/plumbing/object"
|
"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"
|
"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
|
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) {
|
func TestRunArtifactPrepareVersion(t *testing.T) {
|
||||||
|
|
||||||
t.Run("success case - cloud", func(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, &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, "1.2.3", repo.tag)
|
||||||
assert.Equal(t, "428ecf70bc22df0ba3dcf194b5ce53e769abab07", repo.tagHash.String())
|
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) {
|
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))
|
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
|
// Options define build tool specific settings in order to properly retrieve e.g. the version / coordinates of an artifact
|
||||||
type Options struct {
|
type Options struct {
|
||||||
ProjectSettingsFile string
|
ProjectSettingsFile string
|
||||||
DockerImage string
|
DockerImage string
|
||||||
GlobalSettingsFile string
|
GlobalSettingsFile string
|
||||||
M2Path string
|
M2Path string
|
||||||
VersionSource string
|
VersionSource string
|
||||||
VersionSection string
|
VersionSection string
|
||||||
VersionField string
|
VersionField string
|
||||||
VersioningScheme string
|
VersioningScheme string
|
||||||
|
HelmUpdateAppVersion bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utils defines the versioning operations for various build tools
|
// Utils defines the versioning operations for various build tools
|
||||||
@ -106,6 +107,12 @@ func GetArtifact(buildTool, buildDescriptorFilePath string, opts *Options, utils
|
|||||||
default:
|
default:
|
||||||
artifact = &Versionfile{path: buildDescriptorFilePath}
|
artifact = &Versionfile{path: buildDescriptorFilePath}
|
||||||
}
|
}
|
||||||
|
case "helm":
|
||||||
|
artifact = &HelmChart{
|
||||||
|
path: buildDescriptorFilePath,
|
||||||
|
utils: utils,
|
||||||
|
updateAppVersion: opts.HelmUpdateAppVersion,
|
||||||
|
}
|
||||||
case "maven":
|
case "maven":
|
||||||
if len(buildDescriptorFilePath) == 0 {
|
if len(buildDescriptorFilePath) == 0 {
|
||||||
buildDescriptorFilePath = "pom.xml"
|
buildDescriptorFilePath = "pom.xml"
|
||||||
|
@ -1,11 +1,31 @@
|
|||||||
package versioning
|
package versioning
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/SAP/jenkins-library/pkg/mock"
|
||||||
"github.com/stretchr/testify/assert"
|
"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) {
|
func TestGetArtifact(t *testing.T) {
|
||||||
t.Run("custom", func(t *testing.T) {
|
t.Run("custom", func(t *testing.T) {
|
||||||
custom, err := GetArtifact("custom", "test.ini", &Options{VersionField: "theversion", VersionSection: "test"}, nil)
|
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())
|
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) {
|
t.Run("maven", func(t *testing.T) {
|
||||||
opts := Options{
|
opts := Options{
|
||||||
ProjectSettingsFile: "projectsettings.xml",
|
ProjectSettingsFile: "projectsettings.xml",
|
||||||
|
@ -82,6 +82,53 @@ spec:
|
|||||||
- name: gitCredentialsId
|
- name: gitCredentialsId
|
||||||
deprecated: true
|
deprecated: true
|
||||||
params:
|
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
|
- name: buildTool
|
||||||
type: string
|
type: string
|
||||||
description: Defines the tool which is used for building the artifact.
|
description: Defines the tool which is used for building the artifact.
|
||||||
@ -97,6 +144,7 @@ spec:
|
|||||||
- dub
|
- dub
|
||||||
- golang
|
- golang
|
||||||
- gradle
|
- gradle
|
||||||
|
- helm
|
||||||
- maven
|
- maven
|
||||||
- mta
|
- mta
|
||||||
- npm
|
- npm
|
||||||
|
Loading…
x
Reference in New Issue
Block a user