Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/version-bump.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Auto Version Bump

on:
pull_request:
types: [labeled]

jobs:
bump-version:
if: >
github.event.label.name == 'patch' ||
github.event.label.name == 'minor' ||
github.event.label.name == 'major'
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent checkout action version. This workflow uses actions/checkout@v4, but the existing workflow in .github/workflows/main.yml uses actions/checkout@v5. For consistency and to ensure the latest features and security fixes, consider using v5 here as well.

Suggested change
uses: actions/checkout@v4
uses: actions/checkout@v5

Copilot uses AI. Check for mistakes.
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Run version bump script
run: |
python tools/bump_version.py ${{ github.event.label.name }}

- name: Create Pull Request
uses: peter-evans/create-pull-request@v5
with:
commit-message: "chore: bump version (${{ github.event.label.name }})"
title: "chore: bump version (${{ github.event.label.name }})"
body: |
This version bump was triggered by PR #${{ github.event.pull_request.number }}
Label applied: **${{ github.event.label.name }}**
labels: version-bump
base: main
branch: auto-version-bump/${{ github.event.pull_request.number }}
78 changes: 78 additions & 0 deletions tools/bump_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import sys

# The GitHub label passed as an argument: patch, minor, major
LABEL = sys.argv[1]
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling for command-line arguments. If the script is invoked without arguments, it will crash with an IndexError. The script should validate that sys.argv has the required number of arguments and provide a helpful error message if not.

Copilot uses AI. Check for mistakes.

# The file that stores the current version
VERSION_FILE = "version.txt"

def read_version():
"""Read the current version from version.txt"""
with open(VERSION_FILE, "r") as f:
return f.read().strip()
Comment on lines +11 to +12
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling for file operations. If version.txt does not exist or is not readable, the script will crash with a FileNotFoundError. Consider adding try-except blocks to handle these cases gracefully and provide helpful error messages.

Suggested change
with open(VERSION_FILE, "r") as f:
return f.read().strip()
try:
with open(VERSION_FILE, "r") as f:
return f.read().strip()
except FileNotFoundError:
print(f"Error: {VERSION_FILE} not found. Please ensure the version file exists.", file=sys.stderr)
sys.exit(1)
except OSError as e:
print(f"Error reading {VERSION_FILE}: {e}", file=sys.stderr)
sys.exit(1)

Copilot uses AI. Check for mistakes.

def write_version(version):
"""Write the new version back to version.txt"""
with open(VERSION_FILE, "w") as f:
f.write(version + "\n")

def parse_version(v):
"""
Parse a version string like "0.5.0-draft" into parts:
major, minor, patch, and optional suffix (e.g., "-draft").
"""
# Remove suffix if present
v = v.split("-", 1)[0]

parts = v.split(".") # Split into ['0', '5', '0']

major = int(parts[0])
minor = int(parts[1])
patch = int(parts[2])
Comment on lines +27 to +31
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling for malformed version strings. If version.txt contains a version string that doesn't have exactly 3 parts separated by dots (e.g., "0.5" or "0.5.0.1"), the script will crash with an IndexError when trying to access parts[2]. Add validation to ensure the version string is well-formed before parsing.

Copilot uses AI. Check for mistakes.

return major, minor, patch


def bump_version(major, minor, patch):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe instead we could have just 3 new variables, and we should probably throw an exception for unknown LABELs

new_patch = 0
new_minor = 0
new_major = 0

if LABEL == "patch":
    new_patch = patch + 1
elif LABEL == "minor":
    new_minor = minor + 1
elif LABEL == "major":
    new_major = major + 1
else
    raise ExceptionType(f"invalid LABEL: {LABEL}")

return new_major, new_minor, new_patch

Additionally, we may need a suffix or prefix potentially, e.g. "v1.0.0-beta". But I think that can come when we need it. It looks like you started some functionality for suffix, but I dont think it's done.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks you for the feedback!

I understand the suggestion to use three new variables, it does make the bump logic more explicit and clear. I also see your point about throwing an exception for unknown labels. In the current setup, the GitHub workflow only passes predefined labels (patch, minor, major, release), so in practice an exception isn’t strictly necessary.

That said, adding an exception could serve as a useful safeguard if someone were to run the script manually with an invalid label. I’ll update the script to incorporate both of these suggestions.

"""
Increment the version based on LABEL:
- patch: increment patch
- minor: increment minor, reset patch
- major: increment major, reset minor and patch
"""
new_major = major
new_minor = minor
new_patch = patch
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to 'new_patch' is unnecessary as it is redefined before this value is used.
This assignment to 'new_patch' is unnecessary as it is redefined before this value is used.
This assignment to 'new_patch' is unnecessary as it is redefined before this value is used.

Suggested change
new_patch = patch

Copilot uses AI. Check for mistakes.

if LABEL == "patch":
new_patch = patch + 1
elif LABEL == "minor":
new_minor = minor + 1
new_patch = 0
elif LABEL == "major":
new_major = major + 1
new_minor = 0
new_patch = 0
else:
raise ValueError(f"Invalid Label: {LABEL}")
return new_major, new_minor, new_patch
Comment on lines +47 to +58
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing implementation for 'release' label handling. The PR description states "For release labels, the workflow removes the -draft suffix from the version," but there is no code to handle the 'release' label. The script only processes 'patch', 'minor', and 'major' labels, and will raise a ValueError if 'release' is passed.

Copilot uses AI. Check for mistakes.

def main():
# Read current version
old_version = read_version()

# Parse current version
major, minor, patch = parse_version(old_version)

new_major, new_minor, new_patch = bump_version(major, minor, patch)
new_version = f"{new_major}.{new_minor}.{new_patch}"
Comment on lines +67 to +68
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version suffix is being lost during the bump process. The PR description states "Non-release bumps retain any existing suffix (e.g., -draft) while incrementing the appropriate version component," but the current implementation removes the suffix in parse_version (line 25) and never restores it when constructing the new version (line 68). The suffix should be preserved and appended back to the new version string.

Copilot uses AI. Check for mistakes.

# Prevent race conditions by only writing if version changed
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Misleading comment about race condition prevention. The comment claims this check "prevents race conditions," but comparing version strings doesn't prevent race conditions in concurrent workflows. The check only avoids unnecessary writes when the version hasn't changed (which shouldn't happen in normal operation). Consider updating the comment to accurately describe what this check does.

Suggested change
# Prevent race conditions by only writing if version changed
# Only write if the version has changed to avoid unnecessary writes

Copilot uses AI. Check for mistakes.
if new_version != old_version:
write_version(new_version)
print(f"Version updated: {old_version} -> {new_version}")
else:
print(f"No version change needed. Current version: {old_version}")

if __name__ == "__main__":
main()