Skip to content

Commit

Permalink
Merge pull request #32 from cloudogu/feature/30_release_process_funct…
Browse files Browse the repository at this point in the history
…ions

Add release process functions
  • Loading branch information
schnatterer authored Jun 2, 2020
2 parents 89f0532 + fe94b30 commit bd89490
Show file tree
Hide file tree
Showing 11 changed files with 946 additions and 281 deletions.
130 changes: 114 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ Jenkins Pipeline Shared library, that contains additional features for Git, Mave
- [Branches](#branches)
- [SonarCloud](#sonarcloud)
- [Pull Requests in SonarQube](#pull-requests-in-sonarqube)
- [Changelog](#changelog)
- [changelogFileName](#changelogFileName)
- [GitHub](#github)
- [GitFlow](#gitflow)
- [Steps](#steps)
- [mailIfStatusChanged](#mailifstatuschanged)
- [isPullRequest](#ispullrequest)
Expand Down Expand Up @@ -476,38 +480,44 @@ gitWithCreds 'https://your.repo' // Implicitly passed credentials

### Changes to local repository

Note that most changing operations offer parameters to specify an author.
Theses parameters are optional. If not set the author of the last commit will be used as author and committer.
You can specify a different committer by setting the following fields:

* `git.committerName = 'the name'`
* `git.committerEmail = '[email protected]`

It is recommended to set a different committer, so it's obvious those commits were done by Jenkins in the name of
the author. This behaviour is implemented by GitHub for example when committing via the Web UI.

* `git.checkout('branchname')`
* `git.checkoutOrCreate('branchname')` - Creates new Branch if it does not exist
* `git.add('.')`
* `git.commit('message', 'Author', '[email protected])`
* `git.commit('message')` - uses the name and email of the last committer as author and committer.
* `git.commit('message')` - uses default author/committer (see above).
* `git.setTag('tag', 'message', 'Author', '[email protected])`
* `git.setTag('tag', 'message')` - uses the name and email of the last committer as author and committer.
* `git.setTag('tag', 'message')` - uses default author/committer (see above).
* `git.fetch()`
* `git.pull()` - pulls, and in case of merge, uses the name and email of the last committer as author and committer.
* `git.pull()` - pulls, and in case of merge, uses default author/committer (see above).
* `git.pull('refspec')` - pulls specific refspec (e.g. `origin master`), and in case of merge, uses the name and email
of the last committer as author and committer.
* `git.pull('refspec', 'Author', '[email protected])`
* `git.merge('develop', 'Author', '[email protected])`
* `git.merge('develop')` - uses the name and email of the last committer as author and committer.
* `git.merge('develop')` - uses default author/committer (see above).
* `git.mergeFastForwardOnly('master')`

### Changes to remote repository

* `git.push('master')` - pushes origin
* `git.push('origin master')` - pushes origin
**Note**: This always prepends `origin` if not present for historical reasonse (see #44).
That is, right now it is impossible to push other remotes.
This will change in the next major version of ces-build-lib.
This limitation does not apply to other remote-related operations such as `pull()`, `fetch()` and `pushAndPullOnFailure()`
So it's recommended to explicitly mention the origin and not just the refsepc:
* Do: `git.push('origin master')`
* Don't: `git.push('master')` because this will no longer work in the next major version.
* `git.pushAndPullOnFailure('refspec')` - pushes and pulls if push failed e.g. because local and remote have diverged,
then tries pushing again
* `pushGitHubPagesBranch('folderToPush', 'commit Message')` - Commits and pushes a folder to the `gh-pages` branch of
the current repo. Can be used to conveniently deliver websites. See https://pages.github.com. Note:
* Uses the name and email of the last committer as author and committer.
* the `gh-pages` branch is temporarily checked out to the `.gh-pages` folder.
* Don't forget to create a git object with credentials.
* Optional: You can deploy to a sub folder of your GitHub Pages branch using a third parameter
* Examples:
* [cloudogu/continuous-delivery-slides](https://github.com/cloudogu/continuous-delivery-slides/)
* [cloudogu/k8s-security-3-things](https://github.com/cloudogu/k8s-security-3-things)
* See also [Cloudogu Blog: Continuous Delivery with reveal.js](https://cloudogu.com/en/blog/continuous-delivery-with-revealjs)


# Docker

Expand Down Expand Up @@ -792,6 +802,94 @@ So a PR build is treated just like any other. That is,
The Jenkins GitHub Plugin sets `BRANCH_NAME` to the PR Name, e.g. `PR-42`.


# Changelog
Provides the functionality to read changes of a specific version in a changelog that is
based on the changelog format on https://keepachangelog.com/.

Note: The changelog will automatically be formatted. Characters like `"`, `'`, `\` will be removed.
A `\n` will be replaced with `\\n`. This is done to make it possible to pass this string to a json
struct as a value.

Example:

```groovy
Changelog changelog = new Changelog(this)
stage('Changelog') {
String changes = changelog.getChangesForVersion('v1.0.0')
// ...
}
```
## changelogFileName
You can optionally pass the path to the changelog file if it is located somewhere else than in the root path or
if the file name is not `CHANGELOG.md`.

Example:

```groovy
Changelog changelog = new Changelog(this, 'myNewChangelog.md')
stage('Changelog') {
String changes = changelog.getChangesForVersion('v1.0.0')
// ...
}
```

# GitHub
Provides the functionality to do changes on a github repository such as creating a new release.

Example:

```groovy
Git git = new Git(this)
GitHub github = new GitHub(this, git)
stage('Github') {
github.createRelease('v1.1.1', 'Changes for version v1.1.1')
}
```

* `github.createRelease(releaseVersion, changes)` - Creates a release on github.
* Use the `releaseVersion` (String) as name and tag.
* Use the `changes` (String) as body of the release.
* `github.createReleaseWithChangelog(releaseVersion, changelog)` - Creates a release on github.
* Use the `releaseVersion` (String) as name and tag.
* Use the `changelog` (Changelog) to extract the changes out of a changelog and add them to the body of the release.
* `pushPagesBranch('folderToPush', 'commit Message')` - Commits and pushes a folder to the `gh-pages` branch of
the current repo. Can be used to conveniently deliver websites. See https://pages.github.com. Note:
* Uses the name and email of the last committer as author and committer.
* the `gh-pages` branch is temporarily checked out to the `.gh-pages` folder.
* Don't forget to create a git object with credentials.
* Optional: You can deploy to a sub folder of your GitHub Pages branch using a third parameter
* Examples:
* [cloudogu/continuous-delivery-slides](https://github.com/cloudogu/continuous-delivery-slides/)
* [cloudogu/k8s-security-3-things](https://github.com/cloudogu/k8s-security-3-things)
* See also [Cloudogu Blog: Continuous Delivery with reveal.js](https://cloudogu.com/en/blog/continuous-delivery-with-revealjs)


# GitFlow

A wrapper class around the Git class to simplify the use of the git flow branching model.

Example:

```groovy
Git git = new Git(this)
git.committerName = 'jenkins'
git.committerEmail = '[email protected]'
GitFlow gitflow = new GitFlow(this, git)
stage('Gitflow') {
if (gitflow.isReleaseBranch()){
gitflow.finishRelease(git.getSimpleBranchName())
}
}
```

* `gitflow.isReleaseBranch()` - Checks if the currently checked out branch is a gitflow release branch.
* `gitflow.finishRelease(releaseVersion)` - Finishes a git release by merging into develop and master.
* Use the `releaseVersion` (String) as the name of the new git release.

# Steps

## mailIfStatusChanged
Expand Down
83 changes: 83 additions & 0 deletions src/com/cloudogu/ces/cesbuildlib/Changelog.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.cloudogu.ces.cesbuildlib

/**
* Provides the functionality to read changes of a specific version in a changelog that is
* based on the changelog format on https://keepachangelog.com/.
*/
class Changelog implements Serializable {
private script
private String changelogFileName

Changelog(script) {
this(script, 'CHANGELOG.md')
}

Changelog(script, changelogFileName) {
this.script = script
this.changelogFileName = changelogFileName
}

/**
* @return Returns the content of the given changelog.
*/
private String readChangelog(){
script.readFile changelogFileName
}

/**
* Extracts the changes for a given version out of the changelog.
*
* @param releaseVersion The version to get the changes for.
* @return Returns the changes as String.
*/
String changesForVersion(String releaseVersion) {
def changelog = readChangelog()
def start = changesStartIndex(changelog, releaseVersion)
def end = changesEndIndex(changelog, start)
return escapeForJson(changelog.substring(start, end).trim())
}

/**
* Removes characters from a string that could break the json struct when passing the string as json value.
*
* @param string The string to format.
* @return Returns the formatted string.
*/
private static String escapeForJson(String string) {
return string
.replace("\"", "")
.replace("'", "")
.replace("\\", "")
.replace("\n", "\\n")
}

/**
* Returns the start index of changes of a specific release version in the changelog.
*
* @param releaseVersion The version to get the changes for.
* @return Returns the index in the changelog string where the changes start.
*/
private static int changesStartIndex(String changelog, String releaseVersion) {
def index = changelog.indexOf("## [${releaseVersion}]")
if (index == -1){
throw new IllegalArgumentException("The desired version '${releaseVersion}' could not be found in the changelog.")
}
def offset = changelog.substring(index).indexOf("\n")
return index + offset
}

/**
* Returns the end index of changes of a specific release version in the changelog.
*
* @param start The start index of the changes for this version.
* @return Returns the index in the changelog string where the changes end.
*/
private static int changesEndIndex(String changelog, int start) {
def changelogAfterStartIndex = changelog.substring(start)
def index = changelogAfterStartIndex.indexOf("\n## [")
if (index == -1) {
return changelog.length()
}
return index + start
}
}
Loading

0 comments on commit bd89490

Please sign in to comment.