I’m always searching for ways to automate small, repetitive tasks in coding. One of these tasks is increasing the version number and creating a Git tag, which starts a CI build for us. I created the following script to make this process easier.
Python source
#!/usr/bin/env python3
import subprocess
import sys
from pathlib import Path
import re
# --- Config ---
PACKAGE_INIT = Path('mypkg/__init__.py')
REMOTE = 'origin'
# --- Helper functions ---
def run_git(*args, capture_output=True, check=True):
return subprocess.run(['git', *args],
capture_output=capture_output,
text=True,
check=check)
def validate_tag(tag):
if not re.fullmatch(r'[0-9a-z\.\:\-\~]+', tag):
print(f'Error: version tag "{tag}" contains invalid characters.')
print('Version tag should _NOT_ be prefixed with "v" (e.g., only "1.0.0" and not "v1.0.0")')
sys.exit(1)
def tag_exists(tag):
result = run_git('tag', capture_output=True)
return tag in result.stdout.splitlines()
def update_version_file(tag):
text = PACKAGE_INIT.read_text()
new_text = re.sub(
r'__version__\s*=\s*["\'].*?["\']',
f'__version__ = \'{tag}\'',
text
)
PACKAGE_INIT.write_text(new_text)
print(f'[*] Updated __version__ to {tag} in {PACKAGE_INIT}')
# --- Main ---
if len(sys.argv) != 2 or sys.argv[1] in ('-h', '--help', '/?', '-help'):
print(f'Usage: {sys.argv[0]} <version-tag>')
print('<version-tag> should _NOT_ be prefixed with "v" (e.g., only "1.0.0" and not "v1.0.0")')
sys.exit(1)
version_tag = sys.argv[1]
# Validate tag format
validate_tag(version_tag)
# Verify tag does not exist
if tag_exists(version_tag):
print(f'Error: Git tag "{version_tag}" already exists. Use `git tag -l` to view all tags.')
sys.exit(1)
# Update __init__.py
update_version_file(version_tag)
# Commit the change
run_git('add', str(PACKAGE_INIT))
run_git('commit', '-m', f'Bump version to {version_tag}')
print('[*] Committed __version__ change.')
# Create Git tag
run_git('tag', version_tag)
print(f'[*] Created Git tag "{version_tag}".')
# Push commit and tag
run_git('push', REMOTE)
run_git('push', REMOTE, version_tag)
print(f'[*] Pushed commit and tag "{version_tag}" to {REMOTE}.')
To use it, simply call the script and pass in the new version number to set.
$ ./publish_version.py 1.0.2
[*] Updated __version__ to 1.0.2 in mypkg/__init__.py
[*] Committed __version__ change.
[*] Created Git tag "1.0.2".
[*] Pushed commit and tag "1.0.2" to origin.