Advanced example with Semantic versioning, CHangelog auto generation and git commit message syntax enforcement¶
This example is slightly advanced and includes tools that are still being explored. Use with caution!
Dockerfile¶
FROM python:3.8
RUN useradd -u 1000 -M -d /tmp/jenkins jenkins && \
mkdir -p /tmp/jenkins && \
chown -R jenkins /tmp/jenkins && \
ln -s /jenkins/.ssh /tmp/jenkins/.ssh
RUN apt-get update && apt-get install -y npm
RUN npm install -g semantic-release \
@semantic-release/commit-analyzer \
@semantic-release/release-notes-generator \
@semantic-release/changelog \
@semantic-release/git
RUN pip install commitizen
.releaserc¶
{
"branches": ["stable", {"name": "master", "prerelease": true}],
"plugins": [
["@semantic-release/commit-analyzer", {
"releaseRules": [
{"type": "docs", "release": "patch"},
{"type": "style", "release": "patch"},
{"scope": "no-release", "release": false}
],
"parserOpts": {
"noteKeywords": ["BREAKING"]
}
}],
["@semantic-release/release-notes-generator", {
"preset": "angular",
"parserOpts": {
"noteKeywords": ["BREAKING"]
},
"writerOpts": {
"commitsSort": ["subject", "scope"]
}
}],
["@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md"
}
],
["@semantic-release/git", {
"assets": ["CHANGELOG.md"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}]
]
}
.cz.toml¶
[tool.commitizen]
name = "cz_customize"
[tool.commitizen.customize]
message_template = "{{change_type}}{% if scope %}({{scope}}){% endif %}: {{message}}{% if ticket_no %} ({{ticket_no}}){% endif %}"
example = "feature(API): Adding a status endpoint for monitoring and service discovery (MDT-3495)"
schema = "<type>: <body>"
schema_pattern = "^(BREAKING|fix|feat|docs|style|refactor|perf|test|build|ci|chore)(\\(\\w+\\))?: [\\w -.]+(\\((MDT|OP)-[0-9]+\\))?(\\[skip ci\\](.|\\n)*)?$"
[[tool.commitizen.customize.questions]]
type = "list"
name = "change_type"
choices = [
{value = "fix", name = "fix: A bug fix. Correlates with PATCH in SemVer"},
{value = "feat", name = "feat: A new feature. Correlates with MINOR in SemVer"},
{value = "docs", name = "docs: Documentation only changes"},
{value = "style", name = "style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)"},
{value = "refactor", name = "refactor: A code change that neither fixes a bug nor adds a feature"},
{value = "chore", name = "chore: updating Makefile receipe or NPM tasks etc; no production related code change"},
{value = "perf", name = "perf: A code change that improves performance"},
{value = "test", name = "test: Adding missing or correcting existing tests"},
{value = "build", name = "build: Changes that affect the build system or external dependencies (example scopes: pip, docker, npm)"},
{value = "ci", name = "ci: Changes to our CI configuration files and scripts (example scopes: Jenkins, Docker)"},
{value = "BREAKING", name = "BREAKING: A major change."}]
message = "Select the type of change you are committing"
[[tool.commitizen.customize.questions]]
type = "input"
name = "message"
message = "Commit subject"
[[tool.commitizen.customize.questions]]
type = "input"
name = "scope"
message = "Scope, i.e: API (Keep empty to make global)"
[[tool.commitizen.customize.questions]]
type = "input"
name = "ticket_no"
message = "Ticket number i.e: MDT-1234, OP-321 (Keep empty if no ticket)"
Makefile¶
.PHONY: check-git release
check-git:
cz check --rev-range HEAD@{~100}
release:
semantic-release
Jenkinsfile¶
#!/env/bin/env groovy
@Library("m2aJenkins") _
def builder = null
def suffix = "${env.BRANCH_NAME}".replaceAll(/[^a-zA-Z0-9-]/,"-").toLowerCase()
def isNoCICommit = false
pipeline {
agent any
triggers {
bitbucketPush()
}
options {
disableConcurrentBuilds()
ansiColor('xterm')
}
environment {
VERSION = "${artifacts.get_semantic_version()}~${currentBuild.number}~${GIT_COMMIT[0..7]}"
}
stages {
stage("Prepare") {
steps {
script {
utils.build_notify(currentBuild, 'prepare', "Prepare"){
currentBuild.displayName = VERSION
builder = docker.build("m2a-jenkins-snippet:${suffix}")
isNoCICommit = sh(script: "git log --format=format:%s -1", returnStdout: true).trim() ==~ /.+\[skip ci\]$/
}
}
}
}
stage("Check") {
steps {
script {
builder.inside {
utilsLib.build_notify(currentBuild, "checkcommit", "Check commit"){
sh "make check-git"
}
}
}
}
}
stage("Release") {
when {
allOf {
anyOf { branch 'master'; branch 'stable' }
expression { return !isNoCICommit }
}
}
steps {
script {
builder.inside {
utilsLib.build_notify(currentBuild, "release", "Release"){
sshagent(['63351679-e80c-4fb8-8ca0-17d764f863d0']) {
sh "make release"
currentBuild.displayName = "${artifact.get_semantic_version()} - ${currentBuild.number} - ${GIT_COMMIT[0..7]}"
}
return "Version ${artifact.get_semantic_version()} has been released"
}
}
}
}
}
}
post {
cleanup {
cleanWs()
}
}
}