Update Python package version and Git tag

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.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *