diff --git a/cmd/artifactPrepareVersion.go b/cmd/artifactPrepareVersion.go index 0faecaabc..cdd8999e1 100644 --- a/cmd/artifactPrepareVersion.go +++ b/cmd/artifactPrepareVersion.go @@ -232,7 +232,7 @@ func pushChanges(config *artifactPrepareVersionOptions, newVersion string, repos var commitID string - commit, err := addAndCommit(worktree, newVersion, t) + commit, err := addAndCommit(config, worktree, newVersion, t) if err != nil { return commit.String(), err } @@ -312,14 +312,14 @@ func pushChanges(config *artifactPrepareVersionOptions, newVersion string, repos return commitID, nil } -func addAndCommit(worktree gitWorktree, newVersion string, t time.Time) (plumbing.Hash, error) { +func addAndCommit(config *artifactPrepareVersionOptions, worktree gitWorktree, newVersion string, t time.Time) (plumbing.Hash, error) { _, err := worktree.Add(".") if err != nil { return plumbing.Hash{}, errors.Wrap(err, "failed to execute 'git add .'") } //maybe more options are required: https://github.com/go-git/go-git/blob/master/_examples/commit/main.go - commit, err := worktree.Commit(fmt.Sprintf("update version %v", newVersion), &git.CommitOptions{Author: &object.Signature{Name: "Project Piper", When: t}}) + commit, err := worktree.Commit(fmt.Sprintf("update version %v", newVersion), &git.CommitOptions{Author: &object.Signature{Name: config.CommitUserName, When: t}}) if err != nil { return commit, errors.Wrap(err, "failed to commit new version") } diff --git a/cmd/artifactPrepareVersion_generated.go b/cmd/artifactPrepareVersion_generated.go index 33d915dd7..5def04415 100644 --- a/cmd/artifactPrepareVersion_generated.go +++ b/cmd/artifactPrepareVersion_generated.go @@ -17,10 +17,9 @@ import ( type artifactPrepareVersionOptions struct { BuildTool string `json:"buildTool,omitempty"` + CommitUserName string `json:"commitUserName,omitempty"` DockerVersionSource string `json:"dockerVersionSource,omitempty"` FilePath string `json:"filePath,omitempty"` - GitUserEMail string `json:"gitUserEMail,omitempty"` - GitUserName string `json:"gitUserName,omitempty"` GlobalSettingsFile string `json:"globalSettingsFile,omitempty"` IncludeCommitID bool `json:"includeCommitId,omitempty"` M2Path string `json:"m2Path,omitempty"` @@ -75,17 +74,40 @@ func ArtifactPrepareVersionCommand() *cobra.Command { Long: `Prepares and potentially updates the artifact's version before building the artifact. The continuous delivery process requires that each build is done with a unique version number. +There are two common patterns found: -The version generated using this step will contain: +### 1. Continuous Deployment pattern with automatic versioning -* Version (major.minor.patch) from descriptor file in master repository is preserved. Developers should be able to autonomously decide on increasing either part of this version number. -* Timestamp -* CommitId (by default the long version of the hash) +The team has full authority on ` + "`" + `..` + "`" + ` and can increase any part whenever required. +Nonetheless, the automatic versioning makes sure that every build will create a unique version by appending ` + "`" + `..` + "`" + ` with a buildversion (we use a timestamp) and optinally the commitId. -Optionally, but enabled by default, the new version is pushed as a new tag into the source code repository (e.g. GitHub). -If this option is chosen, git credentials and the repository URL needs to be provided. -Since you might not want to configure the git credentials in Jenkins, committing and pushing can be disabled using the ` + "`" + `commitVersion` + "`" + ` parameter as described below. -If you require strict reproducibility of your builds, this should be used.`, +In order to represent this version also in the version control system the new unique version will be pushed with a dedicated tag (` + "`" + `..` + "`" + `). + +Depending on the build tool used and thus the allowed versioning format the ` + "`" + `` + "`" + ` varies. + +**Remarks:** + +* There is no commit to master since this would create a perpetuum mobile and just trigger the next automatic build with automatic versioning, and so on ... +* Not creating a tag would lead to a loss of the final artifact version in scm which often is not acceptable +* You need to ensure that your CI/CD system can push back to your SCM (via providing ssh or HTTP(s) credentials) + +**This pattern is the default** behavior (` + "`" + `versioningType: cloud` + "`" + `) since this is suitable for for most cloud deliveries. + +It is possible to use ` + "`" + `versioningType: cloud_noTag` + "`" + ` which has a slighly different behavior than described above: + +* The new version will NOT be written as tag into the SCM but it is only available in the corresponding CI/CD workspace +* IMPORTANT NOTICE: Using the option ` + "`" + `cloud_noTag` + "`" + ` should not be picked in case you need to ensure a fully traceable path from SCM commit to your build artifact. + +### 2. Pure version ` + "`" + `..` + "`" + ` + +This pattern is often used by teams that have cloud deliveries with no fully automated procedure, e.g. delivery after each takt. +Another typical use-case is development of a library with regular releases where the versioning pattern should be consumable and thus ideally complies to a ` + "`" + `..` + "`" + ` pattern. + +The version is then either manually set by the team in the course of the development process or automatically pushed to master after a successful release. + +Unlike for the _Continuous Deloyment_ pattern descibed above, in this case there is no dedicated tagging required for the build process since the version is already available in the repository. + +Configuration of this pattern is done via ` + "`" + `versioningType: library` + "`" + `.`, PreRunE: func(cmd *cobra.Command, args []string) error { startTime = time.Now() log.SetStepName("artifactPrepareVersion") @@ -114,16 +136,15 @@ If you require strict reproducibility of your builds, this should be used.`, func addArtifactPrepareVersionFlags(cmd *cobra.Command, stepConfig *artifactPrepareVersionOptions) { cmd.Flags().StringVar(&stepConfig.BuildTool, "buildTool", os.Getenv("PIPER_buildTool"), "Defines the tool which is used for building the artifact.") - cmd.Flags().StringVar(&stepConfig.DockerVersionSource, "dockerVersionSource", os.Getenv("PIPER_dockerVersionSource"), "For Docker only: Specifies the source to be used for for generating the automatic version. * This can either be the version of the base image - as retrieved from the `FROM` statement within the Dockerfile, e.g. `FROM jenkins:2.46.2` * Alternatively the name of an environment variable defined in the Docker image can be used which contains the version number, e.g. `ENV MY_VERSION 1.2.3`") - cmd.Flags().StringVar(&stepConfig.FilePath, "filePath", os.Getenv("PIPER_filePath"), "Defines a custom path to the descriptor file. Build tool specific defaults are used (e.g. maven: pom.xml, npm: package.json, mta: mta.yaml)") - cmd.Flags().StringVar(&stepConfig.GitUserEMail, "gitUserEMail", os.Getenv("PIPER_gitUserEMail"), "Allows to overwrite the global git setting 'user.email' available on your Jenkins server.") - cmd.Flags().StringVar(&stepConfig.GitUserName, "gitUserName", os.Getenv("PIPER_gitUserName"), "Allows to overwrite the global git setting 'user.name' available on your Jenkins server.") + 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.DockerVersionSource, "dockerVersionSource", os.Getenv("PIPER_dockerVersionSource"), "For Docker only: Specifies the source to be used for for generating the automatic version. * This can either be the version of the base image - as retrieved from the `FROM` statement within the Dockerfile, e.g. `FROM jenkins:2.46.2` * Alternatively the name of an environment variable defined in the Docker image can be used which contains the version number, e.g. `ENV MY_VERSION 1.2.3`.") + cmd.Flags().StringVar(&stepConfig.FilePath, "filePath", os.Getenv("PIPER_filePath"), "Defines a custom path to the descriptor file. Build tool specific defaults are used (e.g. `maven: pom.xml`, `npm: package.json`, `mta: mta.yaml`).") cmd.Flags().StringVar(&stepConfig.GlobalSettingsFile, "globalSettingsFile", os.Getenv("PIPER_globalSettingsFile"), "Maven only - Path to the mvn settings file that should be used as global settings file.") - cmd.Flags().BoolVar(&stepConfig.IncludeCommitID, "includeCommitId", true, "Defines if the automatically generated version (versioningType 'cloud') should include the commit id hash .") + cmd.Flags().BoolVar(&stepConfig.IncludeCommitID, "includeCommitId", true, "Defines if the automatically generated version (`versioningType: cloud`) should include the commit id hash.") cmd.Flags().StringVar(&stepConfig.M2Path, "m2Path", os.Getenv("PIPER_m2Path"), "Maven only - Path to the location of the local repository that should be used.") - cmd.Flags().StringVar(&stepConfig.Password, "password", os.Getenv("PIPER_password"), "Password/token for git authentication") + cmd.Flags().StringVar(&stepConfig.Password, "password", os.Getenv("PIPER_password"), "Password/token for git authentication.") cmd.Flags().StringVar(&stepConfig.ProjectSettingsFile, "projectSettingsFile", os.Getenv("PIPER_projectSettingsFile"), "Maven only - Path to the mvn settings file that should be used as project settings file.") - cmd.Flags().StringVar(&stepConfig.TagPrefix, "tagPrefix", "build_", "Defines the prefix which is used for the git tag which is written during the versioning run.") + cmd.Flags().StringVar(&stepConfig.TagPrefix, "tagPrefix", "build_", "Defines the prefix which is used for the git tag which is written during the versioning run (only `versioningType: cloud`).") cmd.Flags().StringVar(&stepConfig.Username, "username", os.Getenv("PIPER_username"), "User name for git authentication") cmd.Flags().StringVar(&stepConfig.VersioningTemplate, "versioningTemplate", os.Getenv("PIPER_versioningTemplate"), "DEPRECATED: Defines the template for the automatic version which will be created") cmd.Flags().StringVar(&stepConfig.VersioningType, "versioningType", "cloud", "Defines the type of versioning (`cloud`: fully automatic, `cloud_noTag`: automatic but no tag created, `library`: manual)") @@ -149,6 +170,14 @@ func artifactPrepareVersionMetadata() config.StepData { Mandatory: true, Aliases: []config.Alias{}, }, + { + Name: "commitUserName", + ResourceRef: []config.ResourceReference{}, + Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, + Type: "string", + Mandatory: false, + Aliases: []config.Alias{{Name: "gitUserName"}}, + }, { Name: "dockerVersionSource", ResourceRef: []config.ResourceReference{}, @@ -165,22 +194,6 @@ func artifactPrepareVersionMetadata() config.StepData { Mandatory: false, Aliases: []config.Alias{}, }, - { - Name: "gitUserEMail", - ResourceRef: []config.ResourceReference{}, - Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, - Type: "string", - Mandatory: false, - Aliases: []config.Alias{}, - }, - { - Name: "gitUserName", - ResourceRef: []config.ResourceReference{}, - Scope: []string{"PARAMETERS", "STAGES", "STEPS"}, - Type: "string", - Mandatory: false, - Aliases: []config.Alias{}, - }, { Name: "globalSettingsFile", ResourceRef: []config.ResourceReference{}, diff --git a/cmd/artifactPrepareVersion_test.go b/cmd/artifactPrepareVersion_test.go index 1366dea62..b4a987c53 100644 --- a/cmd/artifactPrepareVersion_test.go +++ b/cmd/artifactPrepareVersion_test.go @@ -469,7 +469,7 @@ func TestPushChanges(t *testing.T) { remote := git.NewRemote(nil, &conf) t.Run("success - username/password", func(t *testing.T) { - config := artifactPrepareVersionOptions{Username: "testUser", Password: "****"} + config := artifactPrepareVersionOptions{Username: "testUser", Password: "****", CommitUserName: "Project Piper"} repo := gitRepositoryMock{remote: remote} worktree := gitWorktreeMock{commitHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3})} @@ -484,7 +484,7 @@ func TestPushChanges(t *testing.T) { }) t.Run("success - ssh fallback", func(t *testing.T) { - config := artifactPrepareVersionOptions{} + config := artifactPrepareVersionOptions{CommitUserName: "Project Piper"} repo := gitRepositoryMock{remote: remote} worktree := gitWorktreeMock{commitHash: plumbing.ComputeHash(plumbing.CommitObject, []byte{1, 2, 3})} diff --git a/documentation/docs/steps/artifactPrepareVersion.md b/documentation/docs/steps/artifactPrepareVersion.md new file mode 100644 index 000000000..abe913be6 --- /dev/null +++ b/documentation/docs/steps/artifactPrepareVersion.md @@ -0,0 +1,25 @@ +# ${docGenStepName} + +## ${docGenDescription} + +## Prerequsites + +none + +## Example + +### Jenkins pipelines + +```groovy +artifactPrepareVersion script: this, buildTool: 'maven' +``` + +### Command line + +``` +piper artifactPrepareVersion --buildTool maven +``` + +## ${docGenParameters} + +## ${docGenConfiguration} diff --git a/documentation/mkdocs.yml b/documentation/mkdocs.yml index c6bf3128a..085e1795f 100644 --- a/documentation/mkdocs.yml +++ b/documentation/mkdocs.yml @@ -35,7 +35,7 @@ nav: - Extensibility: extensibility.md - 'Library steps': - abapEnvironmentPullGitRepo: steps/abapEnvironmentPullGitRepo.md - - artifactSetVersion: steps/artifactSetVersion.md + - artifactPrepareVersion: steps/artifactPrepareVersion.md - batsExecuteTests: steps/batsExecuteTests.md - buildExecute: steps/buildExecute.md - checkChangeInDevelopment: steps/checkChangeInDevelopment.md @@ -94,6 +94,8 @@ nav: - uiVeri5ExecuteTests: steps/uiVeri5ExecuteTests.md - whitesourceExecuteScan: steps/whitesourceExecuteScan.md - xsDeploy: steps/xsDeploy.md + - 'Library Steps (deprecated)': + - artifactSetVersion: steps/artifactSetVersion.md - Resources: - 'Custom Jenkins Setup': customjenkins.md diff --git a/resources/metadata/versioning.yaml b/resources/metadata/versioning.yaml index 6c3736cfe..01bbb57e9 100644 --- a/resources/metadata/versioning.yaml +++ b/resources/metadata/versioning.yaml @@ -9,17 +9,40 @@ metadata: Prepares and potentially updates the artifact's version before building the artifact. The continuous delivery process requires that each build is done with a unique version number. + There are two common patterns found: - The version generated using this step will contain: + ### 1. Continuous Deployment pattern with automatic versioning - * Version (major.minor.patch) from descriptor file in master repository is preserved. Developers should be able to autonomously decide on increasing either part of this version number. - * Timestamp - * CommitId (by default the long version of the hash) + The team has full authority on `..` and can increase any part whenever required. + Nonetheless, the automatic versioning makes sure that every build will create a unique version by appending `..` with a buildversion (we use a timestamp) and optinally the commitId. - Optionally, but enabled by default, the new version is pushed as a new tag into the source code repository (e.g. GitHub). - If this option is chosen, git credentials and the repository URL needs to be provided. - Since you might not want to configure the git credentials in Jenkins, committing and pushing can be disabled using the `commitVersion` parameter as described below. - If you require strict reproducibility of your builds, this should be used. + In order to represent this version also in the version control system the new unique version will be pushed with a dedicated tag (`..`). + + Depending on the build tool used and thus the allowed versioning format the `` varies. + + **Remarks:** + + * There is no commit to master since this would create a perpetuum mobile and just trigger the next automatic build with automatic versioning, and so on ... + * Not creating a tag would lead to a loss of the final artifact version in scm which often is not acceptable + * You need to ensure that your CI/CD system can push back to your SCM (via providing ssh or HTTP(s) credentials) + + **This pattern is the default** behavior (`versioningType: cloud`) since this is suitable for for most cloud deliveries. + + It is possible to use `versioningType: cloud_noTag` which has a slighly different behavior than described above: + + * The new version will NOT be written as tag into the SCM but it is only available in the corresponding CI/CD workspace + * IMPORTANT NOTICE: Using the option `cloud_noTag` should not be picked in case you need to ensure a fully traceable path from SCM commit to your build artifact. + + ### 2. Pure version `..` + + This pattern is often used by teams that have cloud deliveries with no fully automated procedure, e.g. delivery after each takt. + Another typical use-case is development of a library with regular releases where the versioning pattern should be consumable and thus ideally complies to a `..` pattern. + + The version is then either manually set by the team in the course of the development process or automatically pushed to master after a successful release. + + Unlike for the _Continuous Deloyment_ pattern descibed above, in this case there is no dedicated tagging required for the build process since the version is already available in the repository. + + Configuration of this pattern is done via `versioningType: library`. spec: inputs: params: @@ -32,30 +55,26 @@ spec: - PARAMETERS - STAGES - STEPS + - name: commitUserName + aliases: + - name: gitUserName + type: string + description: "Defines the user name which appears in version control for the versioning update (in case `versioningType: cloud`)." + scope: + - PARAMETERS + - STAGES + - STEPS + default: Project Piper - name: dockerVersionSource type: string - description: "For Docker only: Specifies the source to be used for for generating the automatic version. * This can either be the version of the base image - as retrieved from the `FROM` statement within the Dockerfile, e.g. `FROM jenkins:2.46.2` * Alternatively the name of an environment variable defined in the Docker image can be used which contains the version number, e.g. `ENV MY_VERSION 1.2.3`" + description: "For Docker only: Specifies the source to be used for for generating the automatic version. * This can either be the version of the base image - as retrieved from the `FROM` statement within the Dockerfile, e.g. `FROM jenkins:2.46.2` * Alternatively the name of an environment variable defined in the Docker image can be used which contains the version number, e.g. `ENV MY_VERSION 1.2.3`." scope: - PARAMETERS - STAGES - STEPS - name: filePath type: string - description: "Defines a custom path to the descriptor file. Build tool specific defaults are used (e.g. maven: pom.xml, npm: package.json, mta: mta.yaml)" - scope: - - PARAMETERS - - STAGES - - STEPS - - name: gitUserEMail - type: string - description: Allows to overwrite the global git setting 'user.email' available on your Jenkins server. - scope: - - PARAMETERS - - STAGES - - STEPS - - name: gitUserName - type: string - description: Allows to overwrite the global git setting 'user.name' available on your Jenkins server. + description: "Defines a custom path to the descriptor file. Build tool specific defaults are used (e.g. `maven: pom.xml`, `npm: package.json`, `mta: mta.yaml`)." scope: - PARAMETERS - STAGES @@ -72,7 +91,7 @@ spec: - PARAMETERS - name: includeCommitId type: bool - description: Defines if the automatically generated version (versioningType 'cloud') should include the commit id hash . + description: "Defines if the automatically generated version (`versioningType: cloud`) should include the commit id hash." scope: - PARAMETERS - STAGES @@ -90,7 +109,7 @@ spec: - PARAMETERS - name: password type: string - description: Password/token for git authentication + description: Password/token for git authentication. scope: - PARAMETERS - STAGES @@ -107,7 +126,7 @@ spec: - PARAMETERS - name: tagPrefix type: string - description: Defines the prefix which is used for the git tag which is written during the versioning run. + description: "Defines the prefix which is used for the git tag which is written during the versioning run (only `versioningType: cloud`)." scope: - PARAMETERS - STAGES