Once again, the poet’s soul could not stand it and wrote a bicycle. I just didn’t find a ready one.

What does a good release look like?

  • go to the pipeline of the main branch main or the hot release branch release/*
  • we run the Release task there
    • in Github/Gitlab/Gitlab, a release is created (in their terms) with a description of the changes and a tag
  • upon creation of the tag, the release pipeline begins (today it’s not about him at all)

To do this, we need 2 things (the rest is quite trivial):

    • calculate the next version based on the previous one, and from the commit messages understand which of the 3 digits to increase by 1
    • generate release notes based on commit messages.

It is supposed to use this format of commit messages (I do this in the description of MR, not individual commits, except for hot fixes):

- my mega feature
- fix: small fix for other feature

Some description about feature.

How is this different from https://www.conventionalcommits.org/en/v1.0.0 / ? The presence of several header lines. It’s good when you can create a separate test environment for each MR for QA and other developers. Then rule 1 MR - 1 change (feature or bug) is good. When this is not the case, you have to combine several fixes or a new feature and several fixes in one MR. The proposed format is just right for these cases.

How do I get the previous (aka the current) version of the release (we will add to it)?

git describe --tags --abbrev=0 --always > currentVersion.txt

How do I get the current version (you can use it if you deploy it to a test environment)?

git describe --tags --always > currentVersion.txt

To parse commit messages, I had to write a Python script. From the good: this is only 1 file, without library dependencies. So it’s easy to add to any Docker image. For convenience, I made an image with it stepin/git-parse-commits:1.0.0:

git-parse-commits version
git-parse-commits --tag-prefix "v" --tag releaseVersion > nextVersion.txt
git-parse-commits --tag-prefix "v" releaseNotes > releaseNotes.md

Help for the utility (more details in the repository):

git-parse-commits -h

usage: git-parse-commits [-h] [-j] [-t [TAG_PREFIX]] [-s [SCOPE]] [-i [INITIAL_REVISION]] [-l [LAST_REVISION]] [--tag]
                         {version,currentVersion,lastReleaseVersion,releaseVersion,releaseNotes} ...

Provides next release version and release notes from git commit messages.

positional arguments:
  {version,currentVersion,lastReleaseVersion,releaseVersion,releaseNotes}
    version             Prints version of this tool
    currentVersion      Prints current version (useful for non-release builds)
    lastReleaseVersion  Prints version of last release
    releaseVersion      Prints version of next release from git commit messages
    releaseNotes        Prints release notes from git commit messages

options:
  -h, --help            show this help message and exit
  -j, --json            Output in json format
  -t [TAG_PREFIX], --tag-prefix [TAG_PREFIX]
                        prefix for tags (optional)
  -s [SCOPE], --scope [SCOPE]
                        scope to filter release note items
  -i [INITIAL_REVISION], --initial-revision [INITIAL_REVISION]
                        start range from next revision
  -l [LAST_REVISION], --last-revision [LAST_REVISION]
                        stop on this revision
  --tag                 add tag prefix to version (only if tag prefix is defined)

The script is even suitable for mono repositories (changes by components):

git-parse-commits version
git describe --tags --abbrev=0 --match 'component1-*' > currentVersion.txt
git-parse-commits --tag --tag-prefix "component1-" --scope component1 releaseVersion > nextVersion.txt
git-parse-commits --tag-prefix "component1-" --scope component1 releaseNotes > releaseNotes.md

Accordingly, if there are no changes, then releaseNotes.md empty (and you can not release the component).

Well, what might it look like in pipeline using the example of Gitlab:

create_changelog:
  stage: "build"
  image:
      name: "stepin/git-parse-commits:latest"
      entrypoint: [""]
  variables:
      GIT_DEPTH: "0"
  script:
  - git-parse-commits version
  - CURRENT_VERSION="$(git-parse-commits currentVersion)"
  - RELEASE_VERSION="$(git-parse-commits --tag-prefix 'v' releaseVersion)"
  - echo "RELEASE_VERSION=$RELEASE_VERSION\nCURRENT_VERSION=$CURRENT_VERSION" > relNotes.env
  - git-parse-commits --tag-prefix 'v' releaseNotes > releaseNotes.md
  artifacts:
      reports:
          dotenv: relNotes.env
      paths:
      - releaseNotes.md
      expire_in: 1 day
  rules:
  - if: $CI_MERGE_REQUEST_IID
  - if: $CI_COMMIT_REF_NAME == "main" && $CI_PIPELINE_SOURCE != "schedule"
  - if: $CI_COMMIT_REF_NAME == "release/*" && $CI_PIPELINE_SOURCE != "schedule"

release:
  stage: "release"
  image:
      name: "registry.gitlab.com/gitlab-org/release-cli:latest"
      entrypoint: [""]
  script:
  - echo "Release $RELEASE_VERSION"
  release:
      tag_name: "$RELEASE_VERSION"
      tag_message: "Release $RELEASE_VERSION"
      description: "releaseNotes.md"
  assets:
    links:
    - name: "Container Image $CI_COMMIT_TAG"
      url: "https://$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA"
  needs:
  - "create_changelog"
  rules:
  - if: $CI_COMMIT_REF_NAME == "main" && $CI_PIPELINE_SOURCE != "schedule"
    when: manual
    allow_failure: true
  - if: $CI_COMMIT_REF_NAME == "release/*" && $CI_PIPELINE_SOURCE != "schedule"
    when: manual
    allow_failure: true

(CURRENT_VERSION can be used for a non-release build of the project – there will be a unique version)

I note that this automatism is not necessary: you can make a release with your hands in the UI. How to correct his description with your hands.

It is clear that all this is not necessary and can always be done with your hands. Only this leads to a loss of time and minor mistakes. You can do it without them.

Repo: https://github.com/stepin/git-parse-commits