Automating Semantic Versioning Releases
This process was developed after the React/NodeJS process.
This process was developed for a Streamlit app (cctv-apps-streamlit), but will work for any python-based repository.
However, for codebases that will ultimately be built and packaged, there might be better, or at least different, options available.
Use the Automation Scripts when Making a Release
You are "making a release" when you are at a good point to merge code from the
dev branch into master/main.
These merges should always be done via Pull Requests
When you're ready to make a release, follow these steps:
Commit and push all of the relevant code to the
devbranch.Decide what kind of version change this is: Major (large changes, like language version upgrades or version-breaking package upgrades), Minor (new features, non-breaking upgrades), Patch (changes, bug fixes, etc.)
Run the
bump-tag-commit.shscript with the following command (specifying major/minor/patch):./scripts/bump-tag-commit.sh patchOpen a Pull Request (PR) on GitHub to merge the most recent state of
devintomaster.Have someone review the PR.
After the PR is accepted and merged, you (or the reviewer) can run the post-merge script with the command:
python ./scripts/post_merge.py
Add Automation to a new Python Project
These instructions assume your project is starting at version 0.0.1. If you're starting at a different version number, adapt these instructions appropriately.
Install bump-my-version with the following command:
pip install bump-my-versionAdd bump-my-version to your
requirements-dev.txtfile.Under the repository's root directory, add a file called
VERSION(with no file extension). This file should have a single line:0.0.1Under the repository's root directory, add a file called
.bumpversion.tomlwith the following content:[tool.bumpversion]
current_version = "0.0.1"
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
serialize = ["{major}.{minor}.{patch}"]
search = "{current_version}"
replace = "{new_version}"
regex = true
ignore_missing_version = false
ignore_missing_files = false
tag = false
sign_tags = false
tag_name = "v{new_version}"
tag_message = "Bump version: {current_version} → {new_version}"
allow_dirty = false
commit = true
message = "Bump version: {current_version} → {new_version}"
commit_args = ""
setup_hooks = []
pre_commit_hooks = []
post_commit_hooks = []
[[tool.bumpversion.files]]
filename = "VERSION"
search = "{current_version}"
replace = "{new_version}"Under the repository's root directory, add a
scriptsfolder if one doesn't exist.Create a new file under the
scriptsdirectory calledbump-tag-commit.shwith the following content (use the first bash script for Linux or cmd for Windows):#!/bin/bash
# Check if an argument is passed
if [ -z "$1" ]; then
echo "Error: No version bump type provided. Please use 'patch', 'minor', or 'major'."
exit 1
fi
# Ensure the argument is one of patch, minor, or major
if [[ "$1" != "patch" && "$1" != "minor" && "$1" != "major" ]]; then
echo "Error: Invalid version bump type '$1'. Please use 'patch', 'minor', or 'major'."
exit 1
fi
# Bump the version using the provided argument
bump-my-version bump $1
echo "Version bumped and pushed to GitHub."@echo off
:: Check if an argument is passed
if "%1"=="" (
echo Error: No version bump type provided. Please use 'patch', 'minor', or 'major'.
exit /b 1
)
:: Ensure the argument is one of patch, minor, or major
if /i not "%1"=="patch" if /i not "%1"=="minor" if /i not "%1"=="major" (
echo Error: Invalid version bump type '%1'. Please use 'patch', 'minor', or 'major'.
exit /b 1
)
:: Bump the version using the provided argument
bump-my-version bump %1
:: Output success message
echo Version bumped and pushed to GitHub.Create a new file under the
scriptsdirectory calledpost_merge.pywith the following content:import subprocess
def handle_post_merge():
# Read current version from VERSION file
with open('VERSION', 'r') as f:
version = f.read().strip()
# Tag the current version and push the tags to GitHub
subprocess.run(["git", "tag", version])
subprocess.run(["git", "push", "origin", "--tags"])
if __name__ == "__main__":
handle_post_merge()